From 7dcaa2b5ce99d6c6bdb65ab714882c1d56b4e399 Mon Sep 17 00:00:00 2001 From: aaPanel Date: Fri, 16 Dec 2022 10:40:55 +0800 Subject: [PATCH] update to 6.8.27 --- BT-Panel | 165 +- BTPanel/__init__.py | 1007 ++- BTPanel/static/css/files_style.css | 13 +- BTPanel/static/css/files_style.min.css | 2 +- BTPanel/static/css/login.css | 726 +- BTPanel/static/css/site.css | 4863 +++++++------ BTPanel/static/css/xterm.css | 2 +- BTPanel/static/img/ico/index.html | 13 - BTPanel/static/img/index.html | 0 BTPanel/static/img/soft_ico/ico-alioss.png | Bin 516 -> 0 bytes BTPanel/static/img/soft_ico/ico-btyw.png | Bin 1813 -> 0 bytes .../static/img/soft_ico/ico-deployment_jd.png | Bin 553 -> 0 bytes BTPanel/static/img/soft_ico/ico-diskquota.png | Bin 2971 -> 0 bytes BTPanel/static/img/soft_ico/ico-dns.png | Bin 532 -> 0 bytes BTPanel/static/img/soft_ico/ico-domains.png | Bin 6372 -> 0 bytes BTPanel/static/img/soft_ico/ico-idown.png | Bin 21506 -> 0 bytes BTPanel/static/img/soft_ico/ico-jiokong.png | Bin 4741 -> 0 bytes BTPanel/static/img/soft_ico/ico-mail.png | 0 BTPanel/static/img/soft_ico/ico-php-5.6.png | 0 BTPanel/static/img/soft_ico/ico-php-7.2.png | 0 BTPanel/static/img/soft_ico/ico-pure-ftpd.png | Bin 1503 -> 0 bytes BTPanel/static/img/soft_ico/ico-qiniu.png | Bin 630 -> 0 bytes BTPanel/static/img/soft_ico/ico-safelogin.png | Bin 715 -> 0 bytes BTPanel/static/img/soft_ico/ico-ss.png | Bin 3335 -> 0 bytes BTPanel/static/img/soft_ico/ico-yunsuo.png | Bin 2164 -> 0 bytes BTPanel/static/img/soft_ico/ico_idown.png | Bin 21506 -> 0 bytes BTPanel/static/img/soft_ico/index.html | 13 - BTPanel/static/js/config.js | 491 +- BTPanel/static/js/control.js | 275 +- BTPanel/static/js/crontab.js | 913 +-- BTPanel/static/js/database.js | 911 ++- BTPanel/static/js/files.js | 681 +- BTPanel/static/js/files.min.js | 2 +- BTPanel/static/js/index.js | 111 +- BTPanel/static/js/public.js | 232 +- BTPanel/static/js/public_backup.js | 956 ++- BTPanel/static/js/site.js | 6339 ++++++++++------- BTPanel/static/js/soft.js | 61 +- BTPanel/static/js/term.js | 78 +- BTPanel/static/js/tools.js | 1868 ++--- BTPanel/static/language/English/lan.js | 528 +- BTPanel/static/language/English/public.json | 4 +- BTPanel/static/language/English/template.json | 74 +- .../static/language/Simplified_Chinese/lan.js | 4 +- BTPanel/templates/default/autherr.html | 11 +- BTPanel/templates/default/config.html | 922 ++- BTPanel/templates/default/control.html | 1 + BTPanel/templates/default/crontab.html | 57 +- BTPanel/templates/default/database.html | 40 +- BTPanel/templates/default/down.html | 4 +- BTPanel/templates/default/error.html | 2 - BTPanel/templates/default/error3.html | 284 +- BTPanel/templates/default/files.html | 34 +- BTPanel/templates/default/firewall.html | 59 +- BTPanel/templates/default/ftp.html | 550 +- BTPanel/templates/default/index.html | 47 +- BTPanel/templates/default/layout.html | 499 +- BTPanel/templates/default/login.html | 1346 +++- BTPanel/templates/default/site.html | 25 +- BTPanel/templates/default/soft.html | 1 + BTPanel/templates/default/tips.html | 2 +- BTPanel/templates/default/xterm.html | 21 +- class/acme_v2.py | 506 +- class/ajax.py | 443 +- class/apache.py | 72 +- class/cachelib/simple.py | 115 +- class/common.py | 45 +- class/config.py | 794 ++- class/crontab.py | 102 +- class/data.py | 210 +- class/database.py | 917 ++- class/datatool.py | 18 +- class/db.py | 27 +- class/db_mysql.py | 102 +- class/file_execute_deny.py | 22 +- class/files.py | 942 ++- class/firewall_new.py | 86 +- class/firewalld.py | 2 +- class/firewalls.py | 232 +- class/flask_compress.py | 22 +- class/flask_session/sessions.py | 14 +- class/ftp.py | 138 +- class/http_requests.py | 112 +- class/jobs.py | 79 +- class/monitor.py | 136 +- class/nginx.py | 59 +- class/ols.py | 10 +- class/page.py | 86 +- class/panelApi.py | 54 +- class/panelAuth.py | 51 +- class/panelBackup.py | 199 +- class/panelDnsapi.py | 60 +- class/panelLets.py | 176 +- class/panelMessage.py | 55 +- class/panelMysql.py | 35 +- class/panelPHP.py | 13 +- class/panelPlugin.py | 468 +- class/panelProjectController.py | 10 +- class/panelRedirect.py | 60 +- class/panelRun.py | 46 +- class/panelSSL.py | 78 +- class/panelSafe.py | 2 +- class/panelSearch.py | 18 +- class/panelSite.py | 1028 ++- class/panelTask.py | 39 +- class/panelVideo.py | 8 +- class/panelWaf.py | 2 +- class/panelWarning.py | 32 +- class/panel_restore.py | 13 +- class/panel_telegram_bot.py | 2 +- class/plugin_deployment.py | 11 +- class/projectModel/base.py | 63 +- class/projectModel/nodejsModel.py | 142 +- class/public.py | 2326 +++++- class/safe_warning/sw_mysql_port.py | 19 +- class/safe_warning/sw_panel_port.py | 2 +- class/safe_warning/sw_site_spath.py | 2 +- class/send_to_user.py | 58 +- class/setPanelLets.py | 23 +- class/sewer/dns_providers/cloudflare.py | 4 +- class/sewer_Usage.py | 18 +- class/site_dir_auth.py | 107 +- class/sites.py | 2 +- class/ssh_authentication.py | 2 +- class/ssh_security.py | 36 +- class/ssh_terminal.py | 319 +- class/system.py | 67 +- class/tomcat.py | 12 +- class/tools.py | 146 +- class/userlogin.py | 190 +- class/userlogin.py.baiduyun.uploading.cfg | Bin 3034 -> 0 bytes class/wxapp.py | 45 +- config/menu.json | 30 +- data/default.sql | 131 - data/libList.conf | 2 +- data/node.json | 18 +- data/o.pl | 1 - init.sh | 6 +- install/install_soft.sh | 37 +- install/public.sh | 156 +- requirements.txt | 73 +- runconfig.py | 2 +- script/ftp.sh | 6 +- script/panel_ssl_task.py | 3 +- script/rememory.sh | 2 +- task.py | 31 +- tools.py | 87 +- vhost/template/apache/node_http.conf | 1 - 148 files changed, 22281 insertions(+), 13876 deletions(-) delete mode 100644 BTPanel/static/img/ico/index.html delete mode 100644 BTPanel/static/img/index.html delete mode 100644 BTPanel/static/img/soft_ico/ico-alioss.png delete mode 100644 BTPanel/static/img/soft_ico/ico-btyw.png delete mode 100644 BTPanel/static/img/soft_ico/ico-deployment_jd.png delete mode 100644 BTPanel/static/img/soft_ico/ico-diskquota.png delete mode 100644 BTPanel/static/img/soft_ico/ico-dns.png delete mode 100644 BTPanel/static/img/soft_ico/ico-domains.png delete mode 100644 BTPanel/static/img/soft_ico/ico-idown.png delete mode 100644 BTPanel/static/img/soft_ico/ico-jiokong.png delete mode 100644 BTPanel/static/img/soft_ico/ico-mail.png delete mode 100644 BTPanel/static/img/soft_ico/ico-php-5.6.png delete mode 100644 BTPanel/static/img/soft_ico/ico-php-7.2.png delete mode 100644 BTPanel/static/img/soft_ico/ico-pure-ftpd.png delete mode 100644 BTPanel/static/img/soft_ico/ico-qiniu.png delete mode 100644 BTPanel/static/img/soft_ico/ico-safelogin.png delete mode 100644 BTPanel/static/img/soft_ico/ico-ss.png delete mode 100644 BTPanel/static/img/soft_ico/ico-yunsuo.png delete mode 100644 BTPanel/static/img/soft_ico/ico_idown.png delete mode 100644 BTPanel/static/img/soft_ico/index.html delete mode 100644 class/userlogin.py.baiduyun.uploading.cfg delete mode 100644 data/default.sql delete mode 100644 data/o.pl diff --git a/BT-Panel b/BT-Panel index 00e939dd..3659648f 100644 --- a/BT-Panel +++ b/BT-Panel @@ -9,14 +9,44 @@ # +------------------------------------------------------------------- from gevent import monkey monkey.patch_all() -import os,sys,ssl +import os,sys,ssl,time,logging + _PATH = '/www/server/panel' os.chdir(_PATH) +os.system("nohup ./pyenv/bin/python3 class/jobs.py &>/dev/null &") +upgrade_file = 'script/upgrade_flask.sh' +if os.path.exists(upgrade_file): + os.system("nohup bash {} &>/dev/null &".format(upgrade_file)) + +if os.path.exists('class/flask'): + os.system('rm -rf class/flask') + + if not 'class/' in sys.path: sys.path.insert(0,'class/') from BTPanel import app,sys,public is_debug = os.path.exists('data/debug.pl') + +# # 检查加载器 +# def check_plugin_loader(): +# plugin_loader_file = 'class/PluginLoader.so' +# machine = 'x86_64' +# try: +# machine = os.uname().machine +# except: +# pass +# plugin_loader_src_file = "class/PluginLoader.{}.Python3.7.so".format(machine) +# if machine == 'x86_64': +# glibc_version = public.get_glibc_version() +# if glibc_version in ['2.14','2.13','2.12','2.11','2.10']: +# plugin_loader_src_file = "class/PluginLoader.{}.glibc214.Python3.7.so".format(machine) +# if os.path.exists(plugin_loader_src_file): +# os.system("\cp -f {} {}".format(plugin_loader_src_file, plugin_loader_file)) + +# check_plugin_loader() + + if is_debug: import pyinotify,time,logging,re logging.basicConfig(level=logging.DEBUG,format="[%(asctime)s][%(levelname)s] - %(message)s") @@ -26,7 +56,10 @@ if is_debug: _exts = ['py','html','BT-Panel','so'] _explude_patts = [ re.compile('{}/plugin/.+'.format(_PATH)), - re.compile('{}/(tmp|temp)/.+'.format(_PATH)) + re.compile('{}/(tmp|temp)/.+'.format(_PATH)), + re.compile('{}/pyenv/.+'.format(_PATH)), + re.compile('{}/class/projectModel/.+'.format(_PATH)), + re.compile('{}/class/databaseModel/.+'.format(_PATH)) ] _lsat_time = 0 @@ -68,17 +101,53 @@ if is_debug: if not self.is_ext(event.pathname): return self.panel_reload(event.pathname,'[Modify]') + def process_IN_MOVED_TO(self,event): + if not self.is_ext(event.pathname): return + self.panel_reload(event.pathname,'[覆盖]') + def debug_event(): logger.debug('Launch the panel in debug mode') logger.debug('Listening port:0.0.0.0:{}'.format(public.readFile('data/port.pl'))) event = PanelEventHandler() watchManager = pyinotify.WatchManager() - mode = pyinotify.IN_CREATE | pyinotify.IN_DELETE | pyinotify.IN_MODIFY + mode = pyinotify.IN_CREATE | pyinotify.IN_DELETE | pyinotify.IN_MODIFY | pyinotify.IN_MOVED_TO watchManager.add_watch(_PATH, mode, auto_add=True, rec=True) notifier = pyinotify.Notifier(watchManager, event) notifier.loop() +def run_task(): + public.ExecShell("chmod 700 {}/BT-Task".format(_PATH)) + public.ExecShell("{}/BT-Task".format(_PATH)) + +def daemon_task(): + cycle = 60 + task_pid_file = "{}/logs/task.pid".format(_PATH) + while 1: + time.sleep(cycle) + + # 检查pid文件是否存在 + if not os.path.exists(task_pid_file): + continue + + # 读取pid文件 + task_pid = public.readFile(task_pid_file) + if not task_pid: + run_task() + continue + + # 检查进程是否存在 + comm_file = "/proc/{}/comm".format(task_pid) + if not os.path.exists(comm_file): + run_task() + continue + + # 是否为面板进程 + comm = public.readFile(comm_file) + if comm.find('BT-Task') == -1: + run_task() + continue + if __name__ == '__main__': pid_file = "{}/logs/panel.pid".format(_PATH) if os.path.exists(pid_file): @@ -96,13 +165,17 @@ if __name__ == '__main__': sys.stdout.flush() sys.stderr.flush() - f = open('data/port.pl') - PORT = int(f.read()) + + try: + f = open('data/port.pl') + PORT = int(f.read()) + f.close() + if not PORT: PORT = 7800 + except: + PORT = 7800 HOST = '0.0.0.0' if os.path.exists('data/ipv6.pl'): HOST = "0:0:0:0:0:0:0:0" - f.close() - keyfile = 'ssl/privateKey.pem' certfile = 'ssl/certificate.pem' @@ -111,41 +184,83 @@ if __name__ == '__main__': is_ssl = True if not is_ssl or is_debug: - err_f = open('logs/error.log','a+') - os.dup2(err_f.fileno(),sys.stderr.fileno()) - err_f.close() + try: + err_f = open('logs/error.log','a+') + os.dup2(err_f.fileno(),sys.stderr.fileno()) + err_f.close() + except Exception as ex: + print(ex) import threading - import jobs - - job = threading.Thread(target=jobs.control_init) - job.setDaemon(True) - job.start() - + task_thread = threading.Thread(target=daemon_task) + task_thread.setDaemon(True) + task_thread.start() if is_ssl: ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_context.load_cert_chain(certfile=certfile,keyfile=keyfile) - ssl_context.options = (ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1) - ssl_context.set_ciphers("ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE") + if hasattr(ssl_context, "minimum_version"): + ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2 + else: + ssl_context.options = (ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1) + ssl_context.set_ciphers("ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE") + is_ssl_verify = os.path.exists('/www/server/panel/data/ssl_verify_data.pl') + if is_ssl_verify: + crlfile = '/www/server/panel/ssl/crl.pem' + rootcafile = '/www/server/panel/ssl/ca.pem' + #注销列表 + # ssl_context.load_verify_locations(crlfile) + # ssl_context.verify_flags |= ssl.VERIFY_CRL_CHECK_CHAIN + #加载证书 + ssl_context.load_verify_locations(rootcafile) + ssl_context.verify_mode = ssl.CERT_REQUIRED + ssl_context.set_default_verify_paths() + + # 设置日志格式 + _level = logging.WARNING + if is_debug: _level = logging.NOTSET + logging.basicConfig(level=_level,format="[%(asctime)s][%(levelname)s] - %(message)s") + logger = logging.getLogger() + app.logger = logger from gevent.pywsgi import WSGIServer - from geventwebsocket.handler import WebSocketHandler + try: + import flask_sock + if is_ssl: + http_server = WSGIServer((HOST, PORT), app,ssl_context = ssl_context,log=app.logger) + else: + http_server = WSGIServer((HOST, PORT), app,log=app.logger) + except: + from geventwebsocket.handler import WebSocketHandler + if is_ssl: + http_server = WSGIServer((HOST, PORT), app,ssl_context = ssl_context,handler_class=WebSocketHandler,log=app.logger) + else: + http_server = WSGIServer((HOST, PORT), app,handler_class=WebSocketHandler,log=app.logger) - if is_ssl: - http_server = WSGIServer((HOST, PORT), app,handler_class=WebSocketHandler,ssl_context = ssl_context) - else: - http_server = WSGIServer((HOST, PORT), app,handler_class=WebSocketHandler) if is_debug: try: dev = threading.Thread(target=debug_event) - dev.setDaemon(True) dev.start() except: pass - http_server.serve_forever() + is_process = os.path.exists('data/is_process.pl') + if not is_process: + http_server.serve_forever() + else: + http_server.start() + from multiprocessing import Process + def serve_forever(): + http_server.start_accepting() + http_server._stop_event.wait() + process_count = 2 + for i in range(process_count): + p = Process(target=serve_forever) + p.daemon = True + p.start() + while 1: + time.sleep(1000) \ No newline at end of file diff --git a/BTPanel/__init__.py b/BTPanel/__init__.py index 69281bcc..3c8e4947 100644 --- a/BTPanel/__init__.py +++ b/BTPanel/__init__.py @@ -13,6 +13,7 @@ import os import threading import time import re +import uuid import psutil panel_path = '/www/server/panel' if not os.name in ['nt']: @@ -20,21 +21,25 @@ if not os.name in ['nt']: if not 'class/' in sys.path: sys.path.insert(0,'class/') -from flask import Config, Flask, session, render_template, send_file, request, redirect, g, make_response, \ - render_template_string, abort, stream_with_context,Response as Resp +from flask import Flask, session, render_template, send_file, request, redirect, g, make_response, \ + render_template_string, abort,stream_with_context, Response as Resp from cachelib import SimpleCache from werkzeug.wrappers import Response from flask_session import Session from flask_compress import Compress -from flask_sockets import Sockets -cache = SimpleCache() + +cache = SimpleCache(5000) import public # 初始化Flask应用 app = Flask(__name__, template_folder="templates/{}".format(public.GetConfigValue('template'))) Compress(app) -sockets = Sockets(app) +try: + from flask_sock import Sock +except: + from flask_sockets import Sockets as Sock +sockets = Sock(app) # 注册HOOK hooks = {} if not hooks: @@ -44,7 +49,7 @@ dns_client = None app.config['DEBUG'] = os.path.exists('data/debug.pl') app.config['SSL'] = os.path.exists('data/ssl.pl') -#设置BasicAuth +# 设置BasicAuth basic_auth_conf = 'config/basic_auth.json' app.config['BASIC_AUTH_OPEN'] = False if os.path.exists(basic_auth_conf): @@ -53,11 +58,11 @@ if os.path.exists(basic_auth_conf): app.config['BASIC_AUTH_USERNAME'] = ba_conf['basic_user'] app.config['BASIC_AUTH_PASSWORD'] = ba_conf['basic_pwd'] app.config['BASIC_AUTH_OPEN'] = ba_conf['open'] - except: pass + except: + pass -#初始化SESSION服务 -# app.secret_key = uuid.UUID(int=uuid.getnode()).hex[-12:] -app.secret_key = public.md5(str(os.uname())+str(psutil.boot_time())) +# 初始化SESSION服务 +app.secret_key = public.md5(str(os.uname()) + str(psutil.boot_time())) # uuid.UUID(int=uuid.getnode()).hex[-12:] local_ip = None my_terms = {} app.config['SESSION_MEMCACHED'] = SimpleCache(1000,86400) @@ -73,7 +78,6 @@ if app.config['SSL']: else: app.config['SESSION_COOKIE_SAMESITE'] = None - Session(app) import common @@ -90,74 +94,86 @@ 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', - '/bak', - '/monitor', - '/abnormal', - '/close', - '/task', - '/login', - '/config', - '/site', - '/sites', - '/ftp', - '/public', - '/database', - '/data', - '/download_file', - '/control', - '/crontab', - '/firewall', - '/files', - '/soft', - '/ajax', - '/system', - '/panel_data', - '/code', - '/ssl', - '/plugin', - '/wxapp', - '/hook', - '/safe', - '/yield', - '/downloadApi', - '/pluginApi', - '/auth', - '/download', - '/cloud', - '/webssh', - '/connect_event', - '/panel', - '/acme', - '/down', - '/api', - '/tips', - '/message', - '/warning' - ] + '/', + '/san', + '/bak', + '/monitor', + '/abnormal', + '/close', + '/task', + '/login', + '/config', + '/site', + '/sites', + '/ftp', + '/public', + '/database', + '/data', + '/download_file', + '/control', + '/crontab', + '/firewall', + '/files', + '/soft', + '/ajax', + '/system', + '/panel_data', + '/code', + '/ssl', + '/plugin', + '/wxapp', + '/hook', + '/safe', + '/yield', + '/downloadApi', + '/pluginApi', + '/auth', + '/download', + '/cloud', + '/webssh', + '/connect_event', + '/panel', + '/acme', + '/down', + '/api', + '/tips', + '/message', + '/warning' +] if admin_path in admin_path_checks: admin_path = '/bt' +uri_match = re.compile(r"(^/static/[\w_\./\-]+\.(js|css|png|jpg|gif|ico|svg|woff|woff2|ttf|otf|eot|map)$|^/[\w_\./\-]*$)") +session_id_match = re.compile(r"^[\w\.\-]+$") +# ===================================Flask HOOK========================# -#===================================Flask HOOK========================# - -#Flask请求勾子 +# Flask请求勾子 @app.before_request def request_check(): + if request.method not in ['GET','POST']:return abort(404) g.request_time = time.time() # 路由和URI长度过滤 - if len(request.path) > 256: return abort(404) - if len(request.url) > 1024: return abort(404) - - if request.path in ['/service_status']: return - - #POST参数过滤 - if request.path in ['/login','/safe','/hook','/public','/down','/get_app_bind_status','/check_bind']: + if len(request.path) > 256: return abort(403) + if len(request.url) > 1024: return abort(403) + # URI过滤 + if not uri_match.match(request.path): return abort(403) + # POST参数过滤 + if request.path in ['/login', '/safe', '/hook', '/public', '/down', '/get_app_bind_status', '/check_bind']: pdata = request.form.to_dict() for k in pdata.keys(): - if len(k) > 48: return abort(404) - if len(pdata[k]) > 256: return abort(404) + if len(k) > 48: return abort(403) + if len(pdata[k]) > 256: return abort(403) + + # SESSIONID过滤 + session_id = request.cookies.get(app.config['SESSION_COOKIE_NAME'],'') + if session_id and not session_id_match.match(session_id): return abort(403) + + # 请求头过滤 + # if not public.filter_headers(): + # return abort(403) + if session.get('debug') == 1: return + g.get_csrf_html_token_key = public.get_csrf_html_token_key() + if app.config['BASIC_AUTH_OPEN']: if request.path in ['/public', '/download', '/mail_sys', '/hook', '/down', '/check_bind', @@ -172,35 +188,32 @@ def request_check(): if not request.path in ['/safe', '/hook', '/public', '/mail_sys', '/down']: ip_check = public.check_ip_panel() - if ip_check: return abort(401) + if ip_check: return ip_check if request.path.find('/static/') != -1 or request.path == '/code': if not 'login' in session and not 'admin_auth' in session and not 'down' in session: return abort(401) domain_check = public.check_domain_panel() - if domain_check: return abort(401) + 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,'INIT_REQUEST_CHECK_LOCAL_ERR'),json_header + if request.args.get('action') in not_networks: + return public.returnJson(False,'This feature cannot be used in offline mode!'),json_header - if request.path in ['/','/site','/ftp','/database','/soft','/control','/firewall','/files','/xterm','/crontab','/config']: - licenes = 'data/licenes.pl' - if request.path in ['/'] and not os.path.exists(licenes): - return - - # if not public.is_bind(): - # return redirect('/bind',302) + if request.path in ['/site','/ftp','/database','/soft','/control','/firewall','/files','/xterm','/crontab','/config']: + if public.is_error_path(): + return redirect('/error',302) + if not request.path in ['/config']: + if session.get('password_expire',False): + return redirect('/modify_password',302) -#Flask 请求结束勾子 +# Flask 请求结束勾子 @app.teardown_request -def request_end(reques = None): - if request.path in ['/service_status']: return - 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 request_end(reques=None): + if request.method not in ['GET','POST']:return + if not request.path.startswith('/static/'): + public.write_request_log(reques) if 'api_request' in g: if g.api_request: session.clear() @@ -209,6 +222,10 @@ def request_end(reques = None): # Flask 404页面勾子 @app.errorhandler(404) def error_404(e): + if request.method not in ['GET','POST']:return + if not session.get('login',None): + g.auth_error = True + return public.error_not_login() errorStr = ''' 404 Not Found @@ -225,6 +242,10 @@ def error_404(e): # Flask 403页面勾子 @app.errorhandler(403) def error_403(e): + if request.method not in ['GET','POST']:return + if not session.get('login',None): + g.auth_error = True + return public.error_not_login() errorStr = ''' 403 Forbidden @@ -241,10 +262,18 @@ def error_403(e): # Flask 500页面勾子 @app.errorhandler(500) def error_500(e): + if request.method not in ['GET','POST']:return + if not session.get('login',None): + g.auth_error = True + return public.error_not_login() ss = '''404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. During handling of the above exception, another exception occurred:''' error_info = public.get_error_info().strip().split(ss)[-1].strip() + _form = request.form.to_dict() + if 'username' in _form: _form['username'] = '******' + if 'password' in _form: _form['password'] = '******' + if 'phone' in _form: _form['phone'] = '******' request_info = '''REQUEST_DATE: {request_date} PAN_VERSION: {panel_version} OS_VERSION: {os_version} @@ -255,9 +284,9 @@ REQUEST_FORM: {request_form} request_date = public.getDate(), remote_addr = public.GetClientIp(), method = request.method, - full_path = request.full_path, - request_form = request.form.to_dict(), - user_agent = request.headers.get('User-Agent'), + full_path = public.xsssec(request.full_path), + request_form = _form, + user_agent = public.xsssec(request.headers.get('User-Agent')), panel_version = public.version(), os_version = public.get_os_version() ) @@ -281,22 +310,33 @@ def home(): data['databaseCount'] = public.M('databases').count() data['lan'] = public.GetLan('index') data['js_random'] = get_js_random() - public.auto_backup_panel() + public.run_thread(public.auto_backup_panel) return render_template( 'index.html',data = data) @app.route('/xterm',methods = method_all) def xterm(): - #宝塔终端管理 + # 宝塔终端管理 comReturn = comm.local() if comReturn: return comReturn if request.method == method_get[0]: - data = {} - return render_template( 'xterm.html',data = data) + import system + data = system.system().GetConcifInfo() + return render_template('xterm.html', data=data) import ssh_terminal ssh_host_admin = ssh_terminal.ssh_host_admin() - defs = ('get_host_list','get_host_find','modify_host','create_host','remove_host','set_sort','get_command_list','create_command','get_command_find','modify_command','remove_command') - return publicObject(ssh_host_admin,defs,None) + defs = ( + 'get_host_list', 'get_host_find', 'modify_host', 'create_host', 'remove_host', 'set_sort', 'get_command_list', + 'create_command', 'get_command_find', 'modify_command', 'remove_command') + return publicObject(ssh_host_admin, defs, None) +@app.route('/modify_password', methods=method_get) +def modify_password(): + comReturn = comm.local() + if comReturn: return comReturn + # if not session.get('password_expire',False): return redirect('/',302) + data = {} + g.title = public.get_msg_gettext('The password has expired, please change it!') + return render_template('modify_password.html', data=data) @app.route('/site',methods=method_all) def site(pdata = None): @@ -316,12 +356,12 @@ def site(pdata = None): return render_template( 'site.html',data=data) import panelSite siteObject = panelSite.panelSite() - - defs = ('check_del_data','upload_csv','create_website_multiple','del_redirect_multiple','del_proxy_multiple','delete_dir_auth_multiple', + + defs = ('get_auto_restart_rph','remove_auto_restart_rph','auto_restart_rph','check_del_data','upload_csv','create_website_multiple','del_redirect_multiple','del_proxy_multiple','delete_dir_auth_multiple', 'delete_dir_bind_multiple','delete_domain_multiple','set_site_etime_multiple','set_site_php_version_multiple', 'delete_website_multiple','set_site_status_multiple','get_site_err_log','get_site_domains','GetRedirectFile', 'SaveRedirectFile','DeleteRedirect','GetRedirectList','CreateRedirect','ModifyRedirect', - 'set_dir_auth','delete_dir_auth','get_dir_auth','modify_dir_auth_pass', + 'set_dir_auth','delete_dir_auth','get_dir_auth','modify_dir_auth_pass','reset_wp_db', 'GetSiteLogs','GetSiteDomains','GetSecurity','SetSecurity','ProxyCache','CloseToHttps','HttpToHttps','SetEdate', 'SetRewriteTel','GetCheckSafe','CheckSafe','GetDefaultSite','SetDefaultSite','CloseTomcat','SetTomcat','apacheAddPort', 'AddSite','GetPHPVersion','SetPHPVersion','DeleteSite','AddDomain','DelDomain','GetDirBinding','AddDirBinding','GetDirRewrite', @@ -329,7 +369,9 @@ def site(pdata = None): 'SetSiteRunPath','GetSiteRunPath','SetPath','SetIndex','GetIndex','GetDirUserINI','SetDirUserINI','GetRewriteList','SetSSL', 'SetSSLConf','CreateLet','CloseSSLConf','GetSSL','SiteStart','SiteStop','Set301Status','Get301Status','CloseLimitNet','SetLimitNet', 'GetLimitNet','RemoveProxy','GetProxyList','GetProxyDetals','CreateProxy','ModifyProxy','GetProxyFile','SaveProxyFile','ToBackup', - 'DelBackup','GetSitePHPVersion','logsOpen','GetLogsStatus','CloseHasPwd','SetHasPwd','GetHasPwd','GetDnsApi','SetDnsApi') + 'DelBackup','GetSitePHPVersion','logsOpen','GetLogsStatus','CloseHasPwd','SetHasPwd','GetHasPwd','GetDnsApi','SetDnsApi', + 'reset_wp_password','is_update','purge_all_cache','set_fastcgi_cache','update_wp','get_wp_username', + 'get_language','deploy_wp') return publicObject(siteObject,defs,None,pdata) @app.route('/ftp',methods=method_all) @@ -347,7 +389,7 @@ def ftp(pdata = None): return render_template('ftp.html',data=data) import ftp ftpObject = ftp.ftp() - defs = ('AddUser','DeleteUser','SetUserPassword','SetStatus','setPort') + defs = ('AddUser','DeleteUser','SetUserPassword','SetStatus','setPort','set_user_home') return publicObject(ftpObject,defs,None,pdata) @@ -358,12 +400,15 @@ def database(pdata = None): if comReturn: return comReturn if request.method == method_get[0] and not pdata: import ajax - pmd = get_phpmyadmin_dir() + from panelPlugin import panelPlugin session['phpmyadminDir'] = False - if pmd: - session['phpmyadminDir'] = 'http://' + public.GetHost() + ':'+ pmd[1] + '/' + pmd[0] + if panelPlugin().get_phpmyadmin_stat(): + pmd = get_phpmyadmin_dir() + if pmd: + session['phpmyadminDir'] = 'http://' + public.GetHost() + ':' + pmd[1] + '/' + pmd[0] ajax.ajax().set_phpmyadmin_session() - data = {} + import system + data = system.system().GetConcifInfo() data['isSetup'] = os.path.exists(public.GetConfigValue('setup_path') + '/mysql/bin') data['mysql_root'] = public.M('config').where('id=?',(1,)).getField('mysql_root') data['lan'] = public.GetLan('database') @@ -371,11 +416,12 @@ def database(pdata = None): return render_template('database.html',data=data) import database databaseObject = database.database() - defs = ('check_del_data','get_database_size', 'get_mysql_user','check_mysql_ssl_status','write_ssl_to_mysql','GetdataInfo','GetInfo','ReTable','OpTable','AlTable','GetSlowLogs','GetRunStatus', - 'SetDbConf','GetDbStatus','BinLog','GetErrorLog','GetMySQLInfo','SetDataDir','SetMySQLPort', - 'AddDatabase','DeleteDatabase','SetupPassword','ResDatabasePassword','ToBackup','DelBackup', - 'InputSql','SyncToDatabases','SyncGetDatabases','GetDatabaseAccess','SetDatabaseAccess') - return publicObject(databaseObject,defs,None,pdata) + defs = ('GetdataInfo','check_del_data','get_database_size', 'GetInfo', 'ReTable', 'OpTable', 'AlTable', 'GetSlowLogs', 'GetRunStatus', + 'SetDbConf', 'GetDbStatus', 'BinLog', 'GetErrorLog', 'GetMySQLInfo', 'SetDataDir', 'SetMySQLPort','AddCloudDatabase', + 'AddDatabase', 'DeleteDatabase', 'SetupPassword', 'ResDatabasePassword', 'ToBackup', 'DelBackup','AddCloudServer','GetCloudServer','RemoveCloudServer','ModifyCloudServer', + 'InputSql', 'SyncToDatabases', 'SyncGetDatabases', 'GetDatabaseAccess', 'SetDatabaseAccess', + 'get_mysql_user','check_mysql_ssl_status','write_ssl_to_mysql','GetdataInfo') + return publicObject(databaseObject, defs, None, pdata) @app.route('/acme',methods=method_all) @@ -385,9 +431,11 @@ def acme(pdata = None): if comReturn: return comReturn import acme_v2 acme_v2_object = acme_v2.acme_v2() - defs = ('get_orders','remove_order','get_order_find','revoke_order','create_order','get_account_info','set_account_info','update_zip','get_cert_init_api', - 'get_auths','auth_domain','check_auth_status','download_cert','apply_cert','renew_cert','apply_cert_api','apply_dns_auth') - return publicObject(acme_v2_object,defs,None,pdata) + defs = ('get_orders', 'remove_order', 'get_order_find', 'revoke_order', 'create_order', 'get_account_info', + 'set_account_info', 'update_zip', 'get_cert_init_api', + 'get_auths', 'auth_domain', 'check_auth_status', 'download_cert', 'apply_cert', 'renew_cert', + 'apply_cert_api', 'apply_dns_auth') + return publicObject(acme_v2_object, defs, None, pdata) @app.route('/message/',methods=method_all) def message(action = None): @@ -396,74 +444,10 @@ def message(action = None): if comReturn: return comReturn import panelMessage message_object = panelMessage.panelMessage() - defs = ('get_messages','get_message_find','create_message','status_message','remove_message','get_messages_all') - return publicObject(message_object,defs,action,None) + defs = ( + 'get_messages', 'get_message_find', 'create_message', 'status_message', 'remove_message', 'get_messages_all') + return publicObject(message_object, defs, action, None) -@app.route('/colony//',methods=method_all) -def colony_route(module = 'index',action = None): - comReturn = comm.local() - if comReturn: return comReturn - if module in ['os','sys','public']: - return public.returnJson(False,'The specified module does not exist!'),json_header - act_temp = action.split('.') - action = act_temp[0] - if len(act_temp) == 1: act_temp.append('json') - act_type = act_temp[1].lower() - if not act_type in ['json','html','text','txt']: - return public.returnJson(False,'Unsupported response format declaration'),json_header - - #URI输入检测 - if module[:2] == '__' or module[-2:] == '__' or not re.match(r"^\w+$",action): - return public.returnJson(False,'Bad module name!'),json_header - - if action[:2] == '__' or action[-2:] == '__' or not re.match(r"^\w+$",action): - return public.returnJson(False,'Bad module name!'),json_header - - import colony - - #实例化指定模块,并检测模块或方法是否存在 - if not module in colony.__dict__.keys(): - return public.returnJson(False,'The specified module does not exist!'),json_header - obj = eval('colony.{module}.{module}()'.format(module=module)) - act = getattr(obj,action,None) - if act is None: - return public.returnJson(False,'The specified module does not exist!'),json_header - #执行指定方法 - try: - result = act(get_input()) - except: - return public.get_error_info(),text_header - - #响应执行结果 - result_type = type(result) - if result_type in [Response,Resp]: - return result - try: - if act_type == 'json': - return public.GetJson(result),json_header - elif act_type == 'html': - template_name = '{}_{}.html'.format(module,action) - template_file = 'BTPanel/templates/colony/{}'.format(template_name) - if not os.path.exists(template_file): - return public.returnJson(False,'The specified template file was not found!'),json_header - try: - return render_template(template_name,data=result) - except: - return public.get_error_info(),text_header - elif act_type in ['text','txt']: - try: - if result_type == bytes: - result = result.decode('utf-8') - elif result_type in [int,float,list,dict,tuple]: - result = str(result) - return result,text_header - except: - return str(result),text_header - else: - return public.GetJson(result),json_header - - except: - return public.returnJson(False,'Bad response format!'),json_header @@ -474,19 +458,20 @@ def api(pdata=None): if comReturn: return comReturn import panelApi api_object = panelApi.panelApi() - defs = ('get_token','check_bind','get_bind_status','get_apps','add_bind_app','remove_bind_app','set_token','get_tmp_token','get_app_bind_status','login_for_app') - return publicObject(api_object,defs,None,pdata) + defs = ('get_token', 'check_bind', 'get_bind_status', 'get_apps', 'add_bind_app', 'remove_bind_app', 'set_token', + 'get_tmp_token', 'get_app_bind_status', 'login_for_app') + return publicObject(api_object, defs, None, pdata) @app.route('/control',methods=method_all) def control(pdata = None): #监控页面 comReturn = comm.local() if comReturn: return comReturn - if request.method == method_get[0]: - data = {} - data['lan'] = public.GetLan('control') - data['js_random'] = get_js_random() - return render_template( 'control.html',data=data) + import system + data = system.system().GetConcifInfo() + data['lan'] = public.GetLan('control') + data['js_random'] = get_js_random() + return render_template( 'control.html',data=data) @app.route('/firewall',methods=method_all) def firewall(pdata = None): @@ -494,15 +479,16 @@ def firewall(pdata = None): comReturn = comm.local() if comReturn: return comReturn if request.method == method_get[0] and not pdata: - data = {} + import system + data = system.system().GetConcifInfo() data['lan'] = public.GetLan('firewall') data['js_random'] = get_js_random() return render_template( 'firewall.html',data=data) import firewalls firewallObject = firewalls.firewalls() - defs = ('GetList','AddDropAddress','DelDropAddress','FirewallReload','SetFirewallStatus', - 'AddAcceptPort','DelAcceptPort','SetSshStatus','SetPing','SetSshPort','GetSshInfo') - return publicObject(firewallObject,defs,None,pdata) + defs = ('GetList', 'AddDropAddress', 'DelDropAddress', 'FirewallReload', 'SetFirewallStatus', + 'AddAcceptPort', 'DelAcceptPort', 'SetSshStatus', 'SetPing', 'SetSshPort', 'GetSshInfo','SetFirewallStatus') + return publicObject(firewallObject, defs, None, pdata) @app.route('/ssh_security', methods=method_all) @@ -563,9 +549,27 @@ def panel_warning(pdata=None): #首页安全警告 comReturn = comm.local() if comReturn: return comReturn + if 'request_token' in session and 'login' in session: + if not check_csrf(): return public.ReturnJson(False, 'INIT_CSRF_ERR'), json_header + get = get_input() + ikey = 'warning_list' + if get.action == 'get_list': + result = cache.get(ikey) + if not result or 'force' in get: + if 'force' in get: + public.set_module_logs('panelWarning', 'get_list', 1) + result = public.ExecShell("{} {}/script/warning_list.py".format(public.get_python_bin(),public.get_panel_path()))[0] + try: + json.loads(result) + cache.set(ikey, result, 3600) + except: + result = '{"ignore":[],"risk":[],"security":[]}' + return result,json_header import panelWarning dataObject = panelWarning.panelWarning() defs = ('get_list', 'set_ignore', 'check_find') + if get.action in ['set_ignore','check_find']: + cache.delete(ikey) return publicObject(dataObject, defs, None, pdata) @@ -606,25 +610,52 @@ def project(mod_name,def_name): get.action = 'model' get.mod_name = mod_name get.def_name = def_name - return publicObject(project_obj,defs,None,get) -@app.route('/files',methods=method_all) -def files(pdata = None): - #文件管理 +@app.route('/docker', methods=method_all) +def docker(pdata=None): + comReturn = comm.local() + if comReturn: return comReturn + if request.method == method_get[0]: + import system + data = system.system().GetConcifInfo() + data['js_random'] = get_js_random() + data['lan'] = public.GetLan('files') + return render_template('docker.html', data=data) + +@app.route('/dbmodel//', methods=method_all) +def dbmodel(mod_name,def_name): + comReturn = comm.local() + if comReturn: return comReturn + from panelDatabaseController import DatabaseController + database_obj = DatabaseController() + defs = ('model',) + get = get_input() + get.action = 'model' + get.mod_name = mod_name + get.def_name = def_name + + return publicObject(database_obj,defs,None,get) + + + +@app.route('/files', methods=method_all) +def files(pdata=None): + # 文件管理 comReturn = comm.local() if comReturn: return comReturn if request.method == method_get[0] and not request.args.get('path') and not pdata: - data = {} + import system + data = system.system().GetConcifInfo() data['recycle_bin'] = os.path.exists('data/recycle_bin.pl') data['lan'] = public.GetLan('files') data['js_random'] = get_js_random() return render_template('files.html',data=data) import files filesObject = files.files() - defs = ('files_search','files_replace','get_replace_logs', + defs = ('files_search','files_replace','get_replace_logs','get_images_resize','add_files_rsync', 'get_file_attribute','get_file_hash','CreateLink','get_progress','restore_website','fix_permissions','get_all_back', - 'restore_path_permissions','del_path_premissions','get_path_premissions','back_path_permissions', + 'restore_path_permissions','del_path_premissions','get_path_premissions','back_path_permissions','upload_file_exists', 'CheckExistsFiles','GetExecLog','GetSearch','ExecShell','GetExecShellMsg','exec_git','exec_composer','create_download_url', 'UploadFile','GetDir','CreateFile','CreateDir','DeleteDir','DeleteFile','get_download_url_list','remove_download_url','modify_download_url', 'CopyFile','CopyDir','MvFile','GetFileBody','SaveFileBody','Zip','UnZip','get_download_url_find','set_file_ps', @@ -632,7 +663,8 @@ def files(pdata = None): 'GetFileAccess','SetFileAccess','GetDirSize','SetBatchData','BatchPaste','install_rar','get_path_size', 'DownloadFile','GetTaskSpeed','CloseLogs','InstallSoft','UninstallSoft','SaveTmpFile','get_composer_version','exec_composer','update_composer', 'GetTmpFile','del_files_store','add_files_store','get_files_store','del_files_store_types','add_files_store_types','exec_git', - 'RemoveTask','ActionTask','Re_Recycle_bin','Get_Recycle_bin','Del_Recycle_bin','Close_Recycle_bin','Recycle_bin','file_webshell_check','dir_webshell_check' + 'RemoveTask','ActionTask','Re_Recycle_bin','Get_Recycle_bin','Del_Recycle_bin','Close_Recycle_bin','Recycle_bin','file_webshell_check','dir_webshell_check', + 'files_search', 'files_replace', 'get_replace_logs' ) return publicObject(filesObject,defs,None,pdata) @@ -643,7 +675,8 @@ def crontab(pdata = None): comReturn = comm.local() if comReturn: return comReturn if request.method == method_get[0] and not pdata: - data = {} + import system + data = system.system().GetConcifInfo() data['lan'] = public.GetLan('crontab') data['js_random'] = get_js_random() return render_template( 'crontab.html',data=data) @@ -689,6 +722,7 @@ def config(pdata = None): if c_obj.get_ipv6_listen(None): data['ipv6'] = 'checked' if c_obj.get_token(None)['open']: data['api'] = 'checked' data['basic_auth'] = c_obj.get_basic_auth_stat(None) + data['status_code'] = c_obj.get_not_auth_status() data['basic_auth']['value'] = public.getMsg('CLOSED') if data['basic_auth']['open']: data['basic_auth']['value'] = public.getMsg('OPENED') data['debug'] = '' @@ -702,14 +736,14 @@ def config(pdata = None): 'send_by_telegram','set_empty','set_backup_notification','get_panel_ssl_status','set_file_deny', 'del_file_deny', 'get_file_deny', 'get_httpd_access_log_format_parameter','set_httpd_format_log_to_website','get_httpd_access_log_format', 'del_httpd_access_log_format','add_httpd_access_log_format','get_nginx_access_log_format_parameter', - 'set_format_log_to_website','get_nginx_access_log_format','del_nginx_access_log_format', - 'add_nginx_access_log_format','get_ols_private_cache_status','get_ols_value','set_ols_value', + 'set_format_log_to_website','get_nginx_access_log_format','del_nginx_access_log_format','set_click_logs','get_node_config', + 'add_nginx_access_log_format','get_ols_private_cache_status','get_ols_value','set_ols_value','set_node_config', 'get_ols_private_cache','get_ols_static_cache','set_ols_static_cache','switch_ols_private_cache','set_ols_private_cache', 'set_coll_open','get_qrcode_data','check_two_step','set_two_step_auth','create_user','remove_user','modify_user', - 'get_key','get_php_session_path','set_php_session_path','get_cert_source','get_users', + 'get_key','get_php_session_path','set_php_session_path','get_cert_source','get_users','set_request_iptype', 'set_local','set_debug','get_panel_error_logs','clean_panel_error_logs','get_menu_list','set_hide_menu_list', 'get_basic_auth_stat','set_basic_auth','get_cli_php_version','get_tmp_token','get_temp_login','set_temp_login','remove_temp_login','clear_temp_login','get_temp_login_logs', - 'set_cli_php_version','DelOldSession', 'GetSessionCount', 'SetSessionConf', + 'set_cli_php_version','DelOldSession', 'GetSessionCount', 'SetSessionConf','set_not_auth_status', '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', @@ -717,7 +751,8 @@ def config(pdata = None): 'SetTemplates','Set502','setPassword','setUsername','setPanel','setPathInfo','setPHPMaxSize', 'getFpmConfig','setFpmConfig','setPHPMaxTime','syncDate','setPHPDisable','SetControl','get_settings2','del_tg_info','set_tg_bot', 'ClosePanel','AutoUpdatePanel','SetPanelLock','return_mail_list','del_mail_list','add_mail_address','user_mail_send','get_user_mail','set_dingding','get_dingding', - 'get_settings','user_stmp_mail_send','user_dingding_send','get_login_send','set_login_send','clear_login_send','get_login_log','login_ipwhite' + 'get_settings','user_stmp_mail_send','user_dingding_send','get_login_send','set_login_send','clear_login_send','get_login_log','login_ipwhite', + 'set_ssl_verify', 'get_ssl_verify', 'get_password_config', 'set_password_expire', 'set_password_safe' ) return publicObject(config.config(),defs,None,pdata) @@ -728,14 +763,14 @@ def ajax(pdata = None): if comReturn: return comReturn import ajax ajaxObject = ajax.ajax() - defs = ('get_lines','php_info','change_phpmyadmin_ssl_port','set_phpmyadmin_ssl','get_phpmyadmin_ssl', - 'check_user_auth','to_not_beta','get_beta_logs','apple_beta','GetApacheStatus','GetCloudHtml', + defs = ('get_lines','php_info','change_phpmyadmin_ssl_port','set_phpmyadmin_ssl','get_phpmyadmin_ssl','get_pd', + 'check_user_auth','to_not_beta','get_beta_logs','apple_beta','GetApacheStatus','GetCloudHtml','get_pay_type', 'get_load_average','GetOpeLogs','GetFpmLogs','GetFpmSlowLogs','SetMemcachedCache','GetMemcachedStatus', 'GetRedisStatus','GetWarning','SetWarning','CheckLogin','GetSpeed','GetAd','phpSort','ToPunycode', 'GetBetaStatus','SetBeta','setPHPMyAdmin','delClose','KillProcess','GetPHPInfo','GetQiniuFileList','get_process_tops','get_process_cpu_high', 'UninstallLib','InstallLib','SetQiniuAS','GetQiniuAS','GetLibList','GetProcessList','GetNetWorkList', 'GetNginxStatus','GetPHPStatus','GetTaskCount','GetSoftList','GetNetWorkIo','GetDiskIo','GetCpuIo', - 'CheckInstalled','UpdatePanel','GetInstalled','GetPHPConfig','SetPHPConfig') + 'CheckInstalled','UpdatePanel','GetInstalled','GetPHPConfig','SetPHPConfig','log_analysis','speed_log','get_result','get_detailed') return publicObject(ajaxObject,defs,None,pdata) @@ -857,9 +892,9 @@ def download(): if comReturn: return comReturn filename = request.args.get('filename') if filename.find('|') != -1: - filename = filename.split('|')[1] + filename = filename.split('|')[0] #改为获取本地备份 if not filename: return public.ReturnJson(False,"INIT_ARGS_ERR"),json_header - if filename in ['alioss','qiniu','upyun','txcos','ftp','msonedrive','gcloud_storage', 'gdrive', 'aws_s3']: return panel_cloud() + # if filename in ['alioss','qiniu','upyun','txcos','ftp','msonedrive','gcloud_storage', 'gdrive', 'aws_s3']: return panel_cloud() if not os.path.exists(filename): return public.ReturnJson(False,"FILE_NOT_EXISTS"),json_header if request.args.get('play') == 'true': @@ -870,13 +905,22 @@ def download(): mimetype = "application/octet-stream" extName = filename.split('.')[-1] if extName in ['png','gif','jpeg','jpg']: mimetype = None - return send_file(filename,mimetype=mimetype, + import flask + if flask.__version__ < "2.1.0": + return send_file(filename,mimetype=mimetype, as_attachment=True, add_etags=True, conditional=True, attachment_filename=os.path.basename(filename), cache_timeout=0) - + else: + return send_file(filename, mimetype=mimetype, + as_attachment=True, + etag=True, + conditional=True, + download_name=os.path.basename(filename), + max_age=0) +''' @app.route('/cloud',methods=method_all) def panel_cloud(): #从对像存储下载备份文件接口 @@ -891,12 +935,12 @@ def panel_cloud(): plugin_name = get.filename if not os.path.exists('plugin/' + plugin_name + '/' + plugin_name+'_main.py'): - return public.returnJson(False,'INIT_PLUGIN_NOT_EXISTS'),json_header + return public.returnJson(False,'The specified plugin does not exist!'),json_header public.package_path_append('plugin/' + plugin_name) plugin_main = __import__(plugin_name+'_main') public.mod_reload(plugin_main) tmp = eval("plugin_main.%s_main()" % plugin_name) - if not hasattr(tmp,'download_file'): return public.returnJson(False,'INIT_PLUGIN_NOT_DOWN_FUN'),json_header + if not hasattr(tmp,'download_file'): return public.returnJson(False,'Specified plugin has no file download function!'),json_header download_url = tmp.download_file(get.name) if plugin_name == 'ftp': if download_url.find("ftp") != 0:download_url = "ftp://" + download_url @@ -932,6 +976,7 @@ def panel_cloud(): } return redirect(download_url) +''' #======================普通路由区============================# @@ -945,24 +990,30 @@ route_path = os.path.join(admin_path, '') if not route_path: route_path = '/' if route_path[-1] == '/': route_path = route_path[:-1] if route_path[0] != '/': route_path = '/' + route_path -@app.route('/login',methods=method_all) -@app.route(route_path,methods=method_all) -@app.route(route_path + '/',methods=method_all) + + +@app.route('/login', methods=method_all) +@app.route(route_path, methods=method_all) +@app.route(route_path + '/', methods=method_all) def login(): - #面板登录接口 + # 面板登录接口 + if public.is_spider(): return abort(404) if os.path.exists('install.pl'): return redirect('/install') - global admin_check_auth,admin_path,route_path + global admin_check_auth, admin_path, route_path is_auth_path = False - if admin_path != '/bt' and os.path.exists(admin_path_file) and not 'admin_auth' in session: + if admin_path != '/bt' and os.path.exists(admin_path_file) and not 'admin_auth' in session: is_auth_path = True # 登录输入验证 if request.method == method_post[0]: - v_list = ['username','password','code','vcode','cdn_url'] + if is_auth_path: + g.auth_error = True + return public.error_not_login(None) + v_list = ['username', 'password', 'code', 'vcode', 'cdn_url'] for v in v_list: pv = request.form.get(v,'').strip() if v == 'cdn_url': - if len(pv) > 32: return public.returnMsg(False,'PARAMETER_LEN_ERR'),json_header - if not re.match(r"^[\w\.-]+$",pv): public.returnJson(False,'PARAMETER_FORMAT_ERR'),json_header + if len(pv) > 32: return public.return_msg_gettext(False,'Wrong parameter length!'),json_header + if not re.match(r"^[\w\.-]+$",pv): public.return_msg_gettext(False,'Wrong parameter format!'),json_header continue if not pv: continue @@ -970,14 +1021,14 @@ def login(): if v == 'code': p_len = 4 if v == 'vcode': p_len = 6 if len(pv) != p_len: - if v == 'code': return public.returnJson(False,'VCODE_LEN_ERR'),json_header - return public.returnJson(False,'PARAMETER_LEN_ERR'),json_header + if v == 'code': return public.returnJson(False,'Verification code length error!'),json_header + return public.returnJson(False,'Wrong parameter length!'),json_header if not re.match(r"^\w+$",pv): - return public.returnJson(False,'PARAMETER_FORMAT_ERR'),json_header + return public.returnJson(False,'Wrong parameter format!'),json_header for n in request.form.keys(): if not n in v_list: - return public.returnJson(False,'EXTRA_PARAMETER_ERR'),json_header + return public.returnJson(False,'There can be no extra parameters in the login parameters'),json_header get = get_input() import userlogin @@ -992,11 +1043,14 @@ def login(): if session['login'] != False: session['login'] = False cache.set('dologin',True) - public.WriteLog('TYPE_LOGOUT','MANUALLY_LOGOUT',(public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT')),)) + public.write_log_gettext('Logout','Client: {}, has manually exited the panel',(public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT')),)) if 'tmp_login_expire' in session: s_file = 'data/session/{}'.format(session['tmp_login_id']) if os.path.exists(s_file): os.remove(s_file) + token_key = public.get_csrf_html_token_key() + if token_key in session: + del(session[token_key]) session.clear() sess_file = 'data/sess_files/' + public.get_sess_key() if os.path.exists(sess_file): @@ -1004,21 +1058,22 @@ def login(): os.remove(sess_file) except: pass + sess_tmp_file = public.get_full_session_file() + if os.path.exists(sess_tmp_file): os.remove(sess_tmp_file) g.dologin = True - return redirect(login_path) + return redirect(public.get_admin_path()) if is_auth_path: if route_path != request.path and route_path + '/' != request.path: - referer = request.headers.get('Referer','err') + referer = request.headers.get('Referer', 'err') referer_tmp = referer.split('/') referer_path = referer_tmp[-1] if referer_path == '': referer_path = referer_tmp[-2] if route_path != '/' + referer_path: - data = {} - data['lan'] = public.getLan('close') - return abort(404) - # return render_template('autherr.html', data=data) + g.auth_error = True + # return render_template('autherr.html') + return public.error_not_login(None) session['admin_auth'] = True comReturn = common.panelSetup().init() @@ -1030,7 +1085,8 @@ def login(): if request.method == method_get[0]: result = userlogin.userlogin().request_get(get) - if result: return result + if result: + return result data = {} data['lan'] = public.GetLan('login') data['hosts'] = '[]' @@ -1042,7 +1098,11 @@ def login(): else: data['hosts'] = json.dumps(data['hosts']) data['app_login'] = os.path.exists('data/app_login.pl') - return render_template('login.html',data=data ) + public.cache_set(public.Md5(uuid.UUID(int=uuid.getnode()).hex[-12:]+public.GetClientIp()),'check',360) + last_key = 'last_login_token' + session[last_key] = public.GetRandomString(32) + data[last_key] = session[last_key] + return render_template('login.html',data=data) @app.route('/close',methods=method_get) def close(): @@ -1062,6 +1122,12 @@ def close(): def get_app_bind_status(pdata=None): # APP绑定状态查询 if not public.check_app('app_bind'):return abort(404) + get = get_input() + if len(get.__dict__.keys()) > 2: return 'There are meaningless parameters!' + v_list = ['bind_token','data'] + for n in get.__dict__.keys(): + if not n in v_list: + return public.returnJson(False, 'There can be no redundant parameters'), json_header import panelApi api_object = panelApi.panelApi() return json.dumps(api_object.get_app_bind_status(get_input())),json_header @@ -1071,26 +1137,29 @@ def get_app_bind_status(pdata=None): def check_bind(pdata=None): # APP绑定查询 if not public.check_app('app_bind'):return abort(404) + get = get_input() + if len(get.__dict__.keys()) > 4: return 'There are meaningless parameters!' + v_list = ['bind_token','client_brand','client_model','data'] + for n in get.__dict__.keys(): + if not n in v_list: + return public.returnJson(False, 'There can be no redundant parameters'), json_header import panelApi api_object = panelApi.panelApi() return json.dumps(api_object.check_bind(get_input())),json_header + @app.route('/code',methods=method_get) def code(): - if not 'code' in session: - return '' - if not session['code']: - return '' - #获取图片验证码 + if not 'code' in session: return '' + if not session['code']: return '' + # 获取图片验证码 try: - import vilidate,time + import vilidate except: - public.ExecShell("pip install Pillow==5.4.1 -I") + public.ExecShell("pip install Pillow -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() - codeImage = vie.GetCodeImage(80,4) + codeImage = vie.GetCodeImage(80, 4) if sys.version_info[0] == 2: try: from cStringIO import StringIO @@ -1101,57 +1170,64 @@ def code(): from io import BytesIO out = BytesIO() codeImage[0].save(out, "png") - cache.set("codeStr",public.md5("".join(codeImage[1]).lower()),180) - cache.set("codeOut",1,0.1) + cache.set("codeStr", public.md5("".join(codeImage[1]).lower()), 180) + cache.set("codeOut", 1, 0.1) out.seek(0) - return send_file(out, mimetype='image/png', cache_timeout=0) + return send_file(out, mimetype='image/png', max_age=0) -@app.route('/down/',methods=method_all) -def down(token=None,fname=None): - #文件分享对外接口 +@app.route('/down/', methods=method_all) +def down(token=None, fname=None): + # 文件分享对外接口 try: + if public.M('download_token').count()==0:return abort(404) fname = request.args.get('fname') if fname: - if(len(fname) > 256): return abort(404) + if (len(fname) > 256): return abort(404) if fname: fname = fname.strip('/') if not token: return abort(404) - if len(token) != 12: return abort(404) - if not request.args.get('play') in ['true',None,'']: + if len(token) >48: return abort(404) + char_list = ['\\', '/', ':', '*', '?', '"', '<', '>', '|',';','&','`'] + for char in char_list: + if char in token: return abort(404) + if not request.args.get('play') in ['true', None, '']: return abort(404) - - if not re.match(r"^\w+$",token): return abort(404) - find = public.M('download_token').where('token=?',(token,)).find() + args = get_input() + v_list = ['fname', 'play', 'file_password','data'] + for n in args.__dict__.keys(): + if not n in v_list: + return public.returnJson(False, 'There can be no redundant parameters'), json_header + if not re.match(r"^[\w\.]+$", token): return abort(404) + find = public.M('download_token').where('token=?', (token,)).find() if not find: return abort(404) if time.time() > int(find['expire']): return abort(404) if not os.path.exists(find['filename']): return abort(404) if find['password'] and not token in session: - args = get_input() if 'file_password' in args: if not re.match(r"^\w+$",args.file_password): - return public.ReturnJson(False,'WRONG_PASSWD'),json_header + return public.ReturnJson(False,'Wrong password!'),json_header if re.match(r"^\d+$",args.file_password): args.file_password = str(int(args.file_password)) args.file_password += ".0" if args.file_password != str(find['password']): - return public.ReturnJson(False,'WRONG_PASSWD'),json_header + return public.ReturnJson(False,'Wrong password!'),json_header session[token] = 1 session['down'] = True else: pdata = { - "to_path":"", + "to_path": "", "src_path": find['filename'], - "password":True, - "filename":find['filename'].split('/')[-1], + "password": True, + "filename": find['filename'].split('/')[-1], "ps": find['ps'], - "total":find['total'], - "token":find['token'], - "expire":public.format_date(times=find['expire']) - } + "total": find['total'], + "token": find['token'], + "expire": public.format_date(times=find['expire']) + } session['down'] = True - return render_template('down.html',data = pdata) + return render_template('down.html', data=pdata) if not find['password']: session['down'] = True @@ -1162,13 +1238,13 @@ def down(token=None,fname=None): filename = find['filename'] if fname: - filename = os.path.join(filename,fname) - if not public.path_safe_check(fname,False): return abort(404) + filename = os.path.join(filename, fname) + if not public.path_safe_check(fname, False): return abort(404) if os.path.isdir(filename): - return get_dir_down(filename,token,find) + return get_dir_down(filename, token, find) else: if os.path.isdir(filename): - return get_dir_down(filename,token,find) + return get_dir_down(filename, token, find) if request.args.get('play') == 'true': import panelVideo @@ -1177,34 +1253,58 @@ def down(token=None,fname=None): else: mimetype = "application/octet-stream" extName = filename.split('.')[-1] - if extName in ['png','gif','jpeg','jpg']: mimetype = None - return send_file(filename,mimetype=mimetype, - as_attachment=True, - attachment_filename=os.path.basename(filename), - cache_timeout=0) + if extName in ['png', 'gif', 'jpeg', 'jpg']: mimetype = None + b_name = os.path.basename(filename) + return send_file(filename, mimetype=mimetype, + as_attachment=True, + download_name=b_name, + max_age=0) except: return abort(404) -@app.route('/public',methods=method_all) +@app.route('/public', methods=method_all) def panel_public(): get = get_input() if len("{}".format(get.__dict__)) > 1024 * 32: return 'ERROR' + + #获取ping测试 + if 'get_ping' in get: + try: + import panelPing + p = panelPing.Test() + get = p.check(get) + if not get: return 'ERROR' + result = getattr(p,get['act'])(get) + result_type = type(result) + if str(result_type).find('Response') != -1: return result + return public.getJson(result),json_header + except: + return abort(404) + + if public.cache_get(public.Md5(uuid.UUID(int=uuid.getnode()).hex[-12:]+public.GetClientIp()))!='check':return abort(404) + global admin_check_auth, admin_path, route_path, admin_path_file + if admin_path != '/bt' and os.path.exists(admin_path_file) and not 'admin_auth' in session: + return abort(404) + v_list = ['fun', 'name','filename', 'data','secret_key'] + for n in get.__dict__.keys(): + if not n in v_list: + return abort(404) + get.client_ip = public.GetClientIp() num_key = get.client_ip + '_wxapp' if not public.get_error_num(num_key, 10): - return public.returnMsg(False, 'AUTH_FAILED') + return public.return_msg_gettext(False, '10 consecutive authentication failures are prohibited for 1 hour') if not hasattr(get, 'name'): get.name = '' if not hasattr(get, 'fun'): return abort(404) if not public.path_safe_check("%s/%s" % (get.name, get.fun)): return abort(404) if get.fun in ['login_qrcode', 'is_scan_ok','set_login']: # 检查是否验证过安全入口 - global admin_check_auth, admin_path, route_path, admin_path_file if admin_path != '/bt' and os.path.exists(admin_path_file) and not 'admin_auth' in session: return abort(404) #验证是否绑定了设备 - if not public.check_app('app'):return public.returnMsg(False,'UNBOUND_USER') + if not public.check_app('app'):return public.return_msg_gettext(False,'Unbound user') import wxapp pluwx = wxapp.wxapp() checks = pluwx._check(get) @@ -1217,41 +1317,41 @@ def panel_public(): return abort(404) -@app.route('/favicon.ico',methods=method_get) -def send_favicon(): - # 图标 - comReturn = comm.local() - if comReturn: return abort(404) - s_file = '/www/server/panel/BTPanel/static/favicon.ico' - if not os.path.exists(s_file): return abort(404) - return send_file(s_file,conditional=True,add_etags=True) - - -# @app.route('/service_status', methods=method_get) -# def service_status(): -# # 检查面板当前状态 -# try: -# if not 'login' in session: session.clear() -# except: -# pass -# return 'True' - - -@app.route('/coll',methods=method_all) -@app.route('/coll/',methods=method_all) -@app.route('//',methods=method_all) -@app.route('///',methods=method_all) -def panel_other(name=None,fun = None,stype=None): - #插件接口 +@app.route('/coll', methods=method_all) +@app.route('/coll/', methods=method_all) +@app.route('//', methods=method_all) +@app.route('///', methods=method_all) +def panel_other(name=None, fun=None, stype=None): + # 插件接口 + if public.is_error_path(): + return redirect('/error',302) + if not name: return abort(404) + if not re.match(r"^[\w\-]+$", name): return abort(404) + if fun and not re.match(r"^[\w\-\.]+$", fun): return abort(404) if name != "mail_sys" or fun != "send_mail_http.json": comReturn = comm.local() if comReturn: return comReturn + if not stype: + tmp = fun.split('.') + fun = tmp[0] + if len(tmp) == 1: tmp.append('') + stype = tmp[1] if fun: - if fun.find('.json') != -1: - if 'request_token' in session and 'login' in session: - if not check_csrf(): return public.ReturnJson(False, 'INIT_CSRF_ERR'), json_header + if name=='btwaf' and fun=='index': + pass + elif name=='firewall' and fun=='get_file': + pass + elif fun=='static': + pass + elif stype=='html': + pass + else: + if public.get_csrf_cookie_token_key() in session and 'login' in session: + if not check_csrf(): return public.ReturnJson(False, 'CSRF calibration failed, please login again'), json_header args = None else: + p_path = public.get_plugin_path() + '/' + name + if not os.path.exists(p_path): return abort(404) args = get_input() args_list = ['mail_from','password','mail_to','subject','content','subtype','data'] for k in args.__dict__: @@ -1268,11 +1368,21 @@ def panel_other(name=None,fun = None,stype=None): if not name: name = 'coll' if not public.path_safe_check("%s/%s/%s" % (name,fun,stype)): return abort(404) if name.find('./') != -1 or not re.match(r"^[\w-]+$",name): return abort(404) - if not name: return public.returnJson(False,'PLUGIN_INPUT_ERR'),json_header - p_path = os.path.join('/www/server/panel/plugin/', name) + if not name: return public.returnJson(False,'Please pass in the plug-in name!'),json_header + p_path = public.get_plugin_path() + '/' + name if not os.path.exists(p_path): if name == 'btwaf' and fun == 'index': - return render_template('error3.html',data={}) + pdata = {} + import panelPlugin + plu_panel = panelPlugin.panelPlugin() + plugin_list = plu_panel.get_cloud_list() + if not 'pro' in plugin_list: plugin_list['pro'] = -1 + for p in plugin_list['list']: + if p['name'] in ['btwaf']: + if p['endtime'] != 0 and p['endtime'] < time.time(): + pdata['error_msg'] = 1 + break + return render_template('error3.html',data=pdata) return abort(404) #是否响插件应静态文件 @@ -1283,19 +1393,40 @@ def panel_other(name=None,fun = None,stype=None): if not re.match(r"^[\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) + import flask + if flask.__version__ < "2.1.0": + return send_file(s_file,conditional=True,add_etags=True) + else: + return send_file(s_file,conditional=True,etag=True) #准备参数 if not args: args = get_input() args.client_ip = public.GetClientIp() args.fun = fun - + #初始化插件对象 try: is_php = os.path.exists(p_path + '/index.php') if not is_php: - public.package_path_append(p_path) - plugin_main = __import__(name+'_main') + import panelPlugin + plu_panel = panelPlugin.panelPlugin() + plugin_list = plu_panel.get_cloud_list() + waf = 0 + if not 'pro' in plugin_list: plugin_list['pro'] = -1 + for p in plugin_list['list']: + if p['name'] in ['btwaf']: + if p['endtime'] != 0 and p['endtime'] < time.time(): + waf = -1 + try: + public.package_path_append(p_path) + plugin_main = __import__(name+'_main') + if name == 'btwaf' and fun == 'index' and waf == -1 and plugin_list['pro'] == -1: + return render_template('error3.html', data={}) + except: + if name == 'btwaf' and fun == 'index' and waf == -1 and plugin_list['pro'] == -1: + return render_template('error3.html', data={}) + if os.path.exists("{}/btwaf".format(public.get_plugin_path())): + return render_template('error3.html', data={}) try: if sys.version_info[0] == 2: reload(plugin_main) @@ -1305,7 +1436,7 @@ def panel_other(name=None,fun = None,stype=None): except:pass plu = eval('plugin_main.' + name + '_main()') if not hasattr(plu,fun): - return public.returnJson(False,'PLUGIN_NOT_FUN'),json_header + return public.returnJson(False,'Plugin does not exist'),json_header #执行插件方法 if not is_php: @@ -1333,7 +1464,7 @@ def panel_other(name=None,fun = None,stype=None): t_path_root = p_path + '/templates/' t_path = t_path_root + fun + '.html' if not os.path.exists(t_path): - return public.returnJson(False,'PLUGIN_NOT_TEMPLATE'),json_header + return public.returnJson(False,'The specified template does not exist!'),json_header t_body = public.readFile(t_path) #处理模板包含 @@ -1350,9 +1481,12 @@ def panel_other(name=None,fun = None,stype=None): else: #直接响应插件返回值,可以是任意flask支持的响应类型 r_type = type(data) if r_type == dict: - return public.returnJson(False,public.getMsg('PUBLIC_ERR_RETURN').format(r_type)),json_header + if name == 'btwaf' and 'msg' in data: + return render_template('error3.html',data={"error_msg":data['msg']}) + return public.returnJson(False,public.getMsg('Bad return type [{}]').format(r_type)),json_header return data except: + return public.get_error_info() return public.get_error_object(None, plugin_name=name) @@ -1364,12 +1498,12 @@ def panel_hook(): return abort(404) public.package_path_append('plugin/webhook') import webhook_main - #session.clear() return public.getJson(webhook_main.webhook_main().RunHook(get)) @app.route('/install',methods=method_all) def install(): - #初始化面板接口 + # 初始化面板接口 + if not session.get('login',None): return public.error_not_login() if not os.path.exists('install.pl'): return redirect('/login') if public.M('config').where("id=?",('1',)).getField('status') == 1: if os.path.exists('install.pl'): os.remove('install.pl') @@ -1385,15 +1519,15 @@ def install(): data['status'] = os.path.exists('install.pl') data['username'] = public.GetRandomString(8).lower() return render_template( 'install.html',data = data) - + elif request.method == method_post[0]: if not os.path.exists('install.pl'): return redirect(ret_login) get = get_input() - if not hasattr(get,'bt_username'): return public.getMsg('INSTALL_USER_EMPTY') - if not get.bt_username: return public.getMsg('INSTALL_USER_EMPTY') - if not hasattr(get,'bt_password1'): return public.getMsg('INSTALL_PASS_EMPTY') - if not get.bt_password1: return public.getMsg('INSTALL_PASS_EMPTY') - if get.bt_password1 != get.bt_password2: return public.getMsg('INSTALL_PASS_CHECK') + if not hasattr(get,'bt_username'): return public.get_msg_gettext('The user name cannot be empty!') + if not get.bt_username: return public.get_msg_gettext('The user name cannot be empty!') + if not hasattr(get,'bt_password1'): return public.get_msg_gettext('Password can not be blank!') + if not get.bt_password1: return public.get_msg_gettext('Password can not be blank!') + if get.bt_password1 != get.bt_password2: return public.get_msg_gettext('The passwords entered twice do not match, please re-enter!') public.M('users').where("id=?",(1,)).save('username,password', (get.bt_username, public.password_salt(public.md5(get.bt_password1.strip()),uid=1) @@ -1408,7 +1542,9 @@ def install(): # @app.route('/robots.txt',methods=method_all) # def panel_robots(): -# #爬虫规则响应接口 +# # 爬虫规则响应接口 +# get = get_input() +# if len(get.__dict__.keys()) > 1: return abort(404) # robots = '''User-agent: * # Disallow: / # ''' @@ -1427,10 +1563,17 @@ def proxy_rspamd_requests(path): for h in request.headers.keys(): headers[h] = request.headers[h] if request.method == "GET": + import flask if re.search("\.(js|css)$",path): - return send_file('/usr/share/rspamd/www/rspamd/'+path,conditional=True,add_etags=True) + if flask.__version__ < "2.1.0": + return send_file('/usr/share/rspamd/www/rspamd/'+path,conditional=True,add_etags=True) + else: + return send_file('/usr/share/rspamd/www/rspamd/'+path,conditional=True,etag=True) if path == "/": - return send_file('/usr/share/rspamd/www/rspamd/',conditional=True,add_etags=True) + if flask.__version__ < "2.1.0": + return send_file('/usr/share/rspamd/www/rspamd/',conditional=True,add_etags=True) + else: + return send_file('/usr/share/rspamd/www/rspamd/',conditional=True,etag=True) url = "http://127.0.0.1:11334/rspamd/" + path + "?" +param for i in ['stat','auth','neighbours','list_extractors','list_transforms','graph','maps','actions','symbols','history','errors','check_selector','saveactions','savesymbols','getmap']: if i in path: @@ -1474,7 +1617,7 @@ def get_dir_down(filename,token,find): if find['expire'] < (time.time() + (86400 * 365 * 10)): pdata['expire'] = public.format_date(times=find['expire']) else: - pdata['expire'] = public.getMsg('NEVER_EXPIRES') + pdata['expire'] = public.get_msg_gettext('Never Expires') pdata['filename'] = (find['filename'].split('/')[-1] + '/' + to_path).strip('/') return render_template('down.html',data = pdata,to_size=public.to_size) @@ -1522,7 +1665,7 @@ class run_exec: # 模块访问对像 def run(self, toObject, defs, get): result = None - if not get.action in defs: return public.ReturnJson(False, 'ARGS_ERR'), json_header + if not get.action in defs: return public.ReturnJson(False, 'Specific parameters are invalid!'), json_header result = getattr(toObject,get.action)(get) if not hasattr(get, 'html') and not hasattr(get, 's_module'): r_type = type(result) @@ -1535,30 +1678,26 @@ class run_exec: def check_csrf(): - #CSRF校验 + # CSRF校验 if app.config['DEBUG']: return True - request_token = request.cookies.get('request_token') - if session['request_token'] != request_token: return False http_token = request.headers.get('x-http-token') if not http_token: return False - if http_token != session['request_token_head']: return False - cookie_token = request.headers.get('x-cookie-token') - if cookie_token != session['request_token']: return False + if http_token != public.get_csrf_sess_html_token_value(): return False return True -def publicObject(toObject, defs, action=None, get=None): +def publicObject(toObject, defs, action=None, get=None,is_csrf=True): try: # 模块访问前置检查 - if 'request_token' in session and 'login' in session: - if not check_csrf(): return public.ReturnJson(False, 'INIT_CSRF_ERR'), json_header + if is_csrf and public.get_csrf_sess_html_token_value() and session.get('login',None): + if not check_csrf(): return public.ReturnJson(False, 'CSRF calibration failed, please login again'), json_header if not get: get = get_input() if action: get.action = action if hasattr(get, 'path'): get.path = get.path.replace('//', '/').replace('\\', '/') - if get.path.find('./') != -1: return public.ReturnJson(False, 'INIT_PATH_NOT_SAFE'), json_header + if get.path.find('./') != -1: return public.ReturnJson(False, 'Unsafe path'), json_header if get.path.find('->') != -1: get.path = get.path.split('->')[0].strip() get.path = public.xssdecode(get.path) @@ -1574,7 +1713,7 @@ def publicObject(toObject, defs, action=None, get=None): if hasattr(toObject, 'site_path_check'): - if not toObject.site_path_check(get): return public.ReturnJson(False, 'INIT_ACCEPT_NOT'), json_header + if not toObject.site_path_check(get): return public.ReturnJson(False, "Overstepping one authority!"), json_header return run_exec().run(toObject, defs, get) except: return error_500(None) @@ -1583,10 +1722,10 @@ def publicObject(toObject, defs, action=None, get=None): def check_login(http_token=None): #检查是否登录面板 if cache.get('dologin'): return False - if 'login' in session: + if 'login' in session: loginStatus = session['login'] if loginStatus and http_token: - if session['request_token_head'] != http_token: return False + if public.get_csrf_sess_html_token_value() != http_token: return False return loginStatus return False @@ -1594,13 +1733,13 @@ def check_login(http_token=None): def get_pd(): #获取授权信息 tmp = -1 - try: - import panelPlugin - get = public.dict_obj() - get.init = 1 - tmp1 = panelPlugin.panelPlugin().get_cloud_list(get) - except: - tmp1 = None + # try: + # import panelPlugin + # get = public.dict_obj() + # # get.init = 1 + # tmp1 = panelPlugin.panelPlugin().get_cloud_list(get) + # except: + tmp1 = None if tmp1: tmp = tmp1[public.to_string([112,114,111])] ltd = tmp1.get('ltd', -1) @@ -1690,7 +1829,7 @@ def send_authenticated(): #发送http认证信息 request_host = public.GetHost() result = Response('', 401,{'WWW-Authenticate': 'Basic realm="%s"' % request_host.strip()}) - # if not 'login' in session and not 'admin_auth' in session: session.clear() + if not 'login' in session and not 'admin_auth' in session: session.clear() return result @@ -1710,22 +1849,24 @@ def FtpPort(): def is_login(result): - #判断是否登录2 + # 判断是否登录2 if 'login' in session: if session['login'] == True: - result = make_response(result) - request_token = public.GetRandomString(48) - session['request_token'] = request_token - samesite = app.config['SESSION_COOKIE_SAMESITE'] - secure = app.config['SESSION_COOKIE_SECURE'] - if app.config['SSL'] and request.full_path.find('/login?tmp_token=') == 0: - samesite = 'None' - secure = True - result.set_cookie('request_token', request_token, - max_age=86400 * 30, - samesite= samesite, - secure=secure - ) + # result = make_response(result) + # request_token = public.GetRandomString(48) + # request_token_key = public.get_csrf_cookie_token_key() + # session[request_token_key] = request_token + # samesite = app.config['SESSION_COOKIE_SAMESITE'] + # secure = app.config['SESSION_COOKIE_SECURE'] + # if app.config['SSL'] and request.full_path.find('/login?tmp_token=') == 0: + # samesite = 'None' + # secure = True + # result.set_cookie(request_token_key, request_token, + # max_age=86400 * 30, + # samesite= samesite, + # secure=secure + # ) + pass return result # js随机数模板使用,用于不更新版本号时更新前端文件不需要用户强制刷新浏览器 @@ -1736,42 +1877,44 @@ def get_js_random(): public.writeFile('data/js_random.pl',js_random) return js_random -#获取输入数据 +# 获取输入数据 def get_input(): data = public.dict_obj() exludes = ['blob'] for key in request.args.keys(): - data[key] = str(request.args.get(key,'')) + data.set(key,str(request.args.get(key, ''))) try: for key in request.form.keys(): if key in exludes: continue - data[key] = str(request.form.get(key, '')) + data.set(key,str(request.form.get(key, ''))) except: try: post = request.form.to_dict() for key in post.keys(): if key in exludes: continue - data[key] = str(post[key]) + data.set(key, str(post[key])) except: pass if 'form_data' in g: for k in g.form_data.keys(): - data[k] = str(g.form_data[k]) + data.set(k,str(g.form_data[k])) - if not hasattr(data,'data'): data.data = [] + if not hasattr(data, 'data'): data.data = [] return data -#取数据对象 + +# 取数据对象 def get_input_data(data): pdata = public.dict_obj() for key in data.keys(): pdata[key] = str(data[key]) return pdata -#检查Token + +# 检查Token def check_token(data): - #已作废 + # 已作废 pluginPath = 'plugin/safelogin/token.pl' if not os.path.exists(pluginPath): return False from urllib import unquote @@ -1786,7 +1929,9 @@ def check_token(data): if result['token'] != token: return False return result -#======================公共方法区域END============================# + + +# ======================公共方法区域END============================# # --------------------- websocket START -------------------------- # @@ -1823,7 +1968,7 @@ def ws_panel(ws): while True: pdata = ws.receive() - if pdata is '{}': break + if pdata == '{}': break data = json.loads(pdata) get = public.to_dict_obj(data) get._ws = ws @@ -1854,9 +1999,9 @@ def ws_panel_thread(get): 'jobs']: get._ws.send(public.getJson(public.return_status_code(1000, 'Unsafe mod_name, def_name parameter content'))) return - if not hasattr(get, 'args'): - get._ws.send(public.getJson(public.return_status_code(1001, 'args'))) - return + # if not hasattr(get, 'args'): + # get._ws.send(public.getJson(public.return_status_code(1001, 'args'))) + # return mod_file = '{}/{}.py'.format(public.get_class_path(), get.mod_name) if not os.path.exists(mod_file): @@ -1878,7 +2023,7 @@ def ws_panel_thread(get): return result = { 'callback': get.ws_callback, - 'result': _def(public.to_dict_obj(get.args)) + 'result':_def(get) } get._ws.send(public.getJson(result)) @@ -1971,8 +2116,13 @@ def kill_closed(): pids = psutil.pids() keys = sock_pids.copy().keys() for pid in keys: - logging.debug("PID: {} , sock_stat: {}".format(pid, sock_pids[pid].closed)) - if not sock_pids[pid].closed: continue + if hasattr(sock_pids[pid],'closed'): + is_closed = sock_pids[pid].closed + else: + is_closed = not sock_pids[pid].connected + + logging.debug("PID: {} , sock_stat: {}".format(pid,is_closed)) + if not is_closed: continue if pid in pids: try: @@ -1995,7 +2145,7 @@ def sock_recv(cmdstring, ws): stderr=subprocess.PIPE) sock_pids[p.pid] = ws kill_closed() - while p.poll() is None: + while p.poll() == None: send_line = p.stdout.readline().decode() if not send_line or send_line.find('tail: ') != -1: continue ws.send(send_line) @@ -2003,8 +2153,7 @@ def sock_recv(cmdstring, ws): except: kill_closed() - -@app.route('/close_sock_shell', methods=method_all) +@app.route('/close_sock_shell',methods=method_all) def close_sock_shell(): ''' @name 关闭指定命令 @@ -2017,6 +2166,7 @@ def close_sock_shell(): comReturn = comm.local() if comReturn: return comReturn args = get_input() + if not check_csrf(): return public.ReturnJson(False, 'INIT_CSRF_ERR'), json_header cmdstring = args.cmdstring.strip() skey = public.md5(cmdstring) pid = cache.get(skey) @@ -2034,19 +2184,16 @@ def check_csrf_websocket(ws,args): @return void ''' if g.is_aes: return True + if g.api_request: return True + if public.is_debug(): return True is_success = True if not 'x-http-token' in args: is_success = False if is_success: - if session['request_token_head'] != args['x-http-token']: + if public.get_csrf_sess_html_token_value() != args['x-http-token']: is_success = False - # if is_success: - # cookie_token = request.cookies.get('request_token') - # if cookie_token != session['request_token']: - # is_success = False - if not is_success: ws.send('token error') return False @@ -2101,9 +2248,77 @@ def webssh(ws): p = ssh_terminal.ssh_terminal() p.run(ws, ssh_info) del (p) - if not ws.closed: + if ws.connected: ws.close() return 'False' # --------------------- websocket END -------------------------- # + +# @app.route("/daily", methods=method_all) +# def daily(): +# """面板日报数据""" +# +# comReturn = comm.local() +# if comReturn: return comReturn +# +# import panelDaily +# toObject = panelDaily.panelDaily() +# +# defs = ("get_app_usage", "get_daily_data", "get_daily_list") +# result = publicObject(toObject, defs) +# return result + +@app.route('/phpmyadmin/',methods=method_all) +def pma_proxy(path_full = None): + ''' + @name phpMyAdmin代理 + @author hwliang<2022-01-19> + @return Response + ''' + comReturn = comm.local() + if comReturn: return comReturn + cache_key = 'pmd_port_path' + pmd = cache.get(cache_key) + if not pmd: + pmd = get_phpmyadmin_dir() + if not pmd: return 'phpMyAdmin is not installed, please go to the [App Store] page to install it!' + pmd = list(pmd) + cache.set(cache_key,pmd,10) + panel_pool = 'http://' + if request.url_root[:5] == 'https': + panel_pool = 'https://' + import ajax + ssl_info = ajax.ajax().get_phpmyadmin_ssl(None) + if ssl_info['status']: + pmd[1] = ssl_info['port'] + else: + panel_pool = 'http://' + + proxy_url = '{}127.0.0.1:{}/{}/'.format(panel_pool,pmd[1],pmd[0]) + request.full_path.replace('/phpmyadmin/','') + from panelHttpProxy import HttpProxy + px = HttpProxy() + return px.proxy(proxy_url) + +@app.route('/p/',methods=method_all) +@app.route('/p//',methods=method_all) +@app.route('/p//',methods=method_all) +def proxy_port(port,full_path=None): + ''' + @name 代理指定端口 + @author hwliang<2022-01-19> + @return Response + ''' + + comReturn = comm.local() + if comReturn: return comReturn + full_path = request.full_path.replace('/p/{}/'.format(port),'').replace('/p/{}'.format(port),'') + uri = '{}/{}'.format(port,full_path) + uri = uri.replace('//','/') + proxy_url = 'http://127.0.0.1:{}'.format(uri) + from panelHttpProxy import HttpProxy + px = HttpProxy() + return px.proxy(proxy_url) + + + diff --git a/BTPanel/static/css/files_style.css b/BTPanel/static/css/files_style.css index fe49206b..103bf1ca 100644 --- a/BTPanel/static/css/files_style.css +++ b/BTPanel/static/css/files_style.css @@ -132,7 +132,10 @@ } .file_table_view.icon_view .file_ico_type { - height: 60px; + height: 60px; + display: flex; + justify-content: center; + align-items: center; } .file_table_view.icon_view .file_list_content .file_icon { @@ -555,7 +558,13 @@ display: inline-block; position: relative; } - +.file_table_view.list_view .file_list_content .file_ico_type img.file_images{ + width: 22px; + height: 22px; + position: absolute; + top: 50%; + margin-top: -11px; +} .file_table_view.list_view .file_title { vertical-align: top; padding-left: 38px; diff --git a/BTPanel/static/css/files_style.min.css b/BTPanel/static/css/files_style.min.css index b4942c80..c11b0b31 100644 --- a/BTPanel/static/css/files_style.min.css +++ b/BTPanel/static/css/files_style.min.css @@ -1 +1 @@ -.webDelete .vcode #vcodeResult{width:50px;height:26px}*{margin:0;padding:0}.file_table_view.list_view::-webkit-scrollbar{width:12px;height:7px;padding:2px;position:relative}.file_table_view.list_view::-webkit-scrollbar-thumb{border-radius:3px;box-shadow:inset 0 0 6px rgba(0,0,0,.1);background:#bbb}.file_table_view.list_view::-webkit-scrollbar-track{border-radius:3px;box-shadow:inset 0 0 6px rgba(0,0,0,.1);background:#ededed}.file_list_header{height:40px;background:#f6f6f6;border-bottom:none;position:relative;z-index:999;overflow-y:hidden}.file_list_header .file_checkbox,.file_table_view.list_view .file_list_content .file_checkbox{padding:12px!important;margin-left:0!important;cursor:default}.file_list_header .file_checkbox input,.file_table_view.list_view .file_list_content .file_checkbox input{display:none}.file_list_header .file_check,.file_table_view.list_view .file_list_content .file_check{width:16px;height:16px;line-height:16px;border:1px solid #d2d2d2;box-sizing:border-box;border-radius:2px;cursor:pointer;transition:all .2s;background-color:#fff}.file_list_header .file_main_title{line-height:40px;height:40px;overflow:hidden;border:1px solid transparent;border-bottom:none}.file_list_header .file_check i,.file_table_view.list_view .file_list_content .file_check i{position:relative;top:-2px;font-size:14px}.file_table_view.icon_view{position:relative;overflow:hidden}.file_table_view.list_view{position:relative;overflow:auto;z-index:1}.file_table_view .file_name .file_title i:hover{color:#000;cursor:pointer}.file_table_view.list_view .file_name{min-width:120px}.file_table_view.icon_view .file_list_header{display:none}.file_table_view.icon_view .app_menu_group,.file_table_view.icon_view .file_accept,.file_table_view.icon_view .file_checkbox,.file_table_view.icon_view .file_mtime,.file_table_view.icon_view .file_name .iconfont,.file_table_view.icon_view .file_ps,.file_table_view.icon_view .file_size,.file_table_view.icon_view .file_type,.file_table_view.icon_view .file_user{display:none}.file_table_view.icon_view .file_tr{display:inline-block;width:80px!important;min-height:90px;padding:5px;vertical-align:top;margin:0 10px 10px 0}.file_table_view.icon_view .file_name{width:100%!important;position:relative}.file_table_view.icon_view .file_ico_type{height:60px}.file_table_view.icon_view .file_list_content .file_icon{width:45px;height:45px;left:21%}.file_table_view.icon_view .file_name i{font-style:inherit}.file_table_view.icon_view .file_title.file_dir_status,.file_table_view.icon_view .file_title.file_file_status{width:70px;text-align:center;word-break:break-all;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden}.file_table_view.icon_view .file_list_content .file_tr:hover{background:#f0f9f7!important}.file_table_view.icon_view .file_list_content .file_tr.active{background:#f0f9f7!important;border:1px solid #ddefeb;box-sizing:border-box;padding:4px}.file_list_header .file_th{float:left;padding-left:10px;padding-right:10px;height:40px;line-height:40px;font-size:13px;color:#333;position:relative;cursor:pointer;margin-left:-5px}.file_list_header .file_th>span{vertical-align:middle}.file_table_view.list_view .file_type{min-width:80px;max-width:300px}.file_list_header .file_th{color:#333}.file_list_header .file_th.active{background:#eee;color:#555}.file_list_header .file_th:hover{background:#eee}.file_list_header .file_checkbox:hover,.file_list_header .file_operation:hover{background:#f3f3f3}.file_ps_title .set_file_ps{height:25px;line-height:25px;border:1px solid transparent;width:100%;border-radius:2px;padding-left:5px;outline:0}.file_ps_title .set_file_ps:hover{border:1px solid #20a53a!important;background-color:#fff!important}.file_list_header .file_width_resize{float:left;width:10px;margin-left:-5px;height:40px;overflow:hidden;position:relative;z-index:998}.file_list_header .file_width_resize::after{content:'';display:block;background:#e2e2e2;width:1px;height:25px;position:absolute;top:50%;left:50%;margin-left:-1px;margin-top:-12.5px}.file_list_header .file_name{margin-left:0}.file_operation.file_td .set_operation_group{display:none}.file_table_view.list_view .file_tr.active .set_operation_group.is_mobile{display:block!important}.file_list_header .icon_sort{display:inline-block;height:40px;line-height:40px;transition:all .5s;float:right}.file_list_header .icon_sort.active{transform:rotate(180deg)}.file_list_header .icon_sort .iconfont{vertical-align:bottom}.file_table_view.list_view .file_list_shadow{width:100%;height:8px;position:absolute;left:0;right:0;background:-webkit-linear-gradient(top,rgba(220,220,220,.5),rgba(255,255,255,0));z-index:99}.file_table_view.list_view .file_shadow_bottom{background:-webkit-linear-gradient(top,rgba(255,255,255,0),rgba(220,220,220,.5));bottom:0}.align-center{text-align:center}.align-left{text-align:left}.align-right{text-align:right}.btlink{color:#20a53a;text-decoration:none}.file_table_view.list_view .file_list_content{min-width:750px;overflow:auto;border-top:1px solid #ececec}.file_table_view.list_view .file_list_content .file_tr{height:45px;box-sizing:border-box;color:#fff;border-bottom:1px solid #f3f3f3;border-left:1px solid transparent;border-right:1px solid transparent;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file_table_view.list_view .file_list_content .file_tr.editr_tr .file_td{display:none}.file_table_view.list_view .file_list_content .file_tr.editr_tr .file_name{display:inline-block;margin-left:40px}.file_table_view.list_view .file_name .iconfont{display:block}.file_table_view.list_view .file_list_content .file_tr.editr_tr .file_name .iconfont{display:none}.file_table_view.list_view .file_name .icon-favorites,.file_table_view.list_view .file_name .icon-share1{font-size:18px;width:18px;height:18px;vertical-align:middle;position:absolute;top:50%;right:5px;margin-top:-9px;color:#2e973e;display:inline-block}.file_table_view.list_view .file_name .icon-favorites{color:#f5a623;font-size:17px}.file_table_view.list_view .file_name .icon-favorites:before,.file_table_view.list_view .file_name .icon-share1:before{position:relative;top:-16px;height:18px;width:18px;cursor:pointer}.file_table_view.list_view .file_list_content .file_tr.editr_tr .rename_file_input{height:32px;line-height:30px;border:1px solid #cfcfcf;border-radius:2px;padding-left:5px;margin-right:15px;width:150px}.file_table_view.list_view .file_list_content .file_tr.editr_tr .rename_file_input:focus{outline:0;border:1px solid #20a53a}.file_table_view.list_view .file_list_content .file_tr.editr_tr .add_file_btn,.file_table_view.list_view .file_list_content .file_tr.editr_tr .clear_file_btn{height:32px;line-height:30px;padding:0 18px;margin-right:10px;background-color:#20a53a;border-color:#20a53a;border-radius:2px;color:#fff;border:1px solid transparent}.file_table_view.list_view .file_list_content .file_tr.editr_tr .clear_file_btn{color:#555;background-color:#fff;border-color:#ccc}.file_table_view.list_view .file_list_content .file_tr.editr_tr .add_file_btn::focus,.file_table_view.list_view .file_list_content .file_tr.editr_tr .clear_file_btn::focus{outline:0}.file_table_view.list_view .file_list_content .file_tr.editr_tr .add_file_btn:hover{color:#fff;background-color:#10952a;border-color:#398439}.file_table_view.list_view .file_ps .foo_menu{display:none}.file_table_view.list_view .file_tr:hover .foo_menu{display:inline-block;position:absolute;right:10px}.file_table_view.list_view .file_tr .foo_menu_title{position:relative;padding-right:10px}.file_table_view.list_view .file_tr .foo_menu_title i{display:inline-block;width:7px;height:7px;color:#20a53a;border-left:1px solid;border-bottom:1px solid;transform:rotate(-45deg);position:absolute;top:3px;right:0}.file_table_view.list_view .file_tr.active .foo_menu{display:inline-block;position:absolute;right:10px}.file_table_view.list_view .file_tr.active .foo_menu .foo_menu_title,.file_table_view.list_view .file_tr:hover .foo_menu .foo_menu_title{border:1px solid #20a53a;color:#20a53a!important;background:#fff;border-radius:2px;padding:4px 18px 4px 8px;font-size:12px;cursor:pointer;position:relative}.file_table_view.list_view .file_tr .foo_menu .foo_menu_title:hover,.file_table_view.list_view .file_tr.active .foo_menu .foo_menu_title:hover{border:1px solid #20a53a;color:#fff!important;background:#20a53a}.file_table_view.list_view .file_tr .foo_menu.foo_menu_click .foo_menu_title,.file_table_view.list_view .file_tr.active .foo_menu .foo_menu_title:hover,.file_table_view.list_view .file_tr:hover .foo_menu .foo_menu_title:hover{border:1px solid #20a53a;color:#fff!important;background:#20a53a}.file_table_view.list_view .file_tr .foo_menu.foo_menu_click .foo_menu_title::after,.file_table_view.list_view .file_tr.active .foo_menu .foo_menu_title:hover::after,.file_table_view.list_view .file_tr:hover .foo_menu .foo_menu_title:hover::after{color:#fff}.app_menu_group ul{color:#20a53a;height:50px;line-height:50px;display:inline-block;margin-left:8px}.app_menu_group ul li{display:inline-block;margin-right:10px}.app_menu_group ul li i,.app_menu_group ul li.separate,.file_table_view.list_view .file_list_content .file_tr.app_menu_operation .file_td:nth-child(n+3){display:none}.file_table_view.list_view .file_list_content .file_tr:last-child{border-bottom:1px solid #ececec}.file_table_view.list_view .file_list_content .file_tr.active,.file_table_view.list_view .file_list_content .file_tr:hover{background:#f0f9f7!important;border:1px solid #ddefeb;border-top:none}.file_table_view.list_view .file_list_content .file_tr:hover .set_operation_group{display:block}.file_table_view.list_view .file_list_content .file_tr.active .set_file_ps,.file_table_view.list_view .file_list_content .file_tr:hover .set_file_ps{border:1px solid transparent;background-color:#eff9f7}.file_table_view.list_view .file_list_content .file_td{height:45px;line-height:45px;padding:0 8px;color:#777;overflow:hidden;position:relative;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block;vertical-align:top}.file_table_view.list_view .file_list_content .file_td.file_operation{text-overflow:clip;text-align:right}.file_table_view.list_view .file_list_content .file_checkbox{padding:14px 12px!important}.file_table_view.list_view .file_list_content .file_ico_type{height:40px;line-height:40px;display:inline-block;position:relative}.file_table_view.list_view .file_title{vertical-align:top;padding-left:38px;width:100%;box-sizing:border-box;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.file_table_view.list_view .file_title.file_file_status i{font-style:normal}.file_table_view.list_view .file_title i{font-style:normal;cursor:pointer;color:#555}.file_table_view.list_view .file_title i:hover{color:#20a53a}.file_edit_input{height:31px;display:inline-block;width:100%;padding-left:5px;outline:0;border:1px solid #ebf3f1;margin-left:-5px;border-radius:2px}.file_list_content .file_icon{width:100%;width:30px;height:30px;position:absolute;top:7.5px;background-repeat:no-repeat;background-size:cover;background-position:center;image-rendering:-webkit-optimize-contrast;background-image:url(../img/file_icon/file.png);cursor:pointer}.file_list_content .file_html{background-image:url(../img/file_icon/html.png)}.file_list_content .file_css{background-image:url(../img/file_icon/css.png)}.file_list_content .file_js{background-image:url(../img/file_icon/javascript.png)}.file_list_content .file_json{background-image:url(../img/file_icon/json.png)}.file_list_content .file_folder{background-image:url(../img/file_icon/folder.png)}.file_list_content .file_php{background-image:url(../img/file_icon/php.png)}.file_list_content .file_log{background-image:url(../img/file_icon/log.png)}.file_list_content .file_txt{background-image:url(../img/file_icon/file.png)}.file_list_content .file_py{background-image:url(../img/file_icon/python.png);width:30px;height:30px;left:2.5px;top:10px}.file_list_content .file_png{background-image:url(../img/file_icon/png.png)}.file_list_content .file_lua{background-image:url(../img/file_icon/lua.png);width:30px;height:30px;left:2.5px;top:10px}.file_list_content .file_access{background-image:url(../img/file_icon/access.png)}.file_list_content .file_apk{background-image:url(../img/file_icon/apk.png)}.file_list_content .file_avi{background-image:url(../img/file_icon/avi.png)}.file_list_content .file_bmp{background-image:url(../img/file_icon/bmp.png)}.file_list_content .file_cdr{background-image:url(../img/file_icon/cdr.png)}.file_list_content .file_gif{background-image:url(../img/file_icon/gif.png)}.file_list_content .file_ico{background-image:url(../img/file_icon/ico.png)}.file_list_content .file_java{background-image:url(../img/file_icon/java.png)}.file_list_content .file_jpeg{background-image:url(../img/file_icon/jpeg.png)}.file_list_content .file_jpg{background-image:url(../img/file_icon/jpg.png)}.file_list_content .file_mkv{background-image:url(../img/file_icon/mkv.png)}.file_list_content .file_mov{background-image:url(../img/file_icon/mov.png)}.file_list_content .file_mp4{background-image:url(../img/file_icon/mp4.png)}.file_list_content .file_mpeg{background-image:url(../img/file_icon/mpeg.png)}.file_list_content .file_mpg{background-image:url(../img/file_icon/mpg.png)}.file_list_content .file_pdf{background-image:url(../img/file_icon/pdf.png)}.file_list_content .file_ppt,.file_list_content .file_pptx{background-image:url(../img/file_icon/ppt.png);width:30px;height:30px;left:2.5px}.file_list_content .file_rar{background-image:url(../img/file_icon/rar.png)}.file_list_content .file_rm{background-image:url(../img/file_icon/rm.png)}.file_list_content .file_rmvb{background-image:url(../img/file_icon/rmvb.png)}.file_list_content .file_sql{background-image:url(../img/file_icon/sql.png)}.file_list_content .file_swf{background-image:url(../img/file_icon/swf.png)}.file_list_content .file_tar{background-image:url(../img/file_icon/tar.png)}.file_list_content .file_webm{background-image:url(../img/file_icon/webm.png)}.file_list_content .file_webp{background-image:url(../img/file_icon/webp.png)}.file_list_content .file_wma{background-image:url(../img/file_icon/wma.png)}.file_list_content .file_wmv{background-image:url(../img/file_icon/wmv.png)}.file_list_content .file_doc,.file_list_content .file_docx{background-image:url(../img/file_icon/doc.png);width:30px;height:30px;left:2.5px}.file_list_content .file_xls,.file_list_content .file_xlsx{background-image:url(../img/file_icon/xls.png);width:30px;height:30px;left:2.5px}.file_list_content .file_xml{background-image:url(../img/file_icon/xml.png)}.file_list_content .file_zip{background-image:url(../img/file_icon/zip.png)}.file_list_content .file_gz,.file_list_content .file_tar{background-image:url(../img/file_icon/tar.png)}.content_right_menu,.file_right_menu,.selection_right_menu{padding:5px 0;width:190px;border:1px solid #dadada;box-shadow:5px 5px 12px rgba(0,0,0,.15);position:fixed;left:-100%;top:-100%;z-index:9999;background:#fff}.content_right_menu li,.file_right_menu li,.selection_right_menu li{list-style-type:none;margin:0;padding:0;height:30px;font-size:12px;color:#555;line-height:30px;padding-left:48px;box-sizing:border-box;position:relative;font-weight:400;border-radius:2px}.selection_right_menu li span{color:#666}.content_right_menu li.separate,.file_right_menu li.separate,.selection_right_menu li.separate{height:1px;background:#ececec}.content_right_menu li:hover,.file_right_menu li:hover,.selection_right_menu li:hover{background:#efefef;cursor:pointer}.selection_right_menu li:hover .file_menu_down .set_group{display:inline-block}.content_right_menu li .file_menu_icon,.file_right_menu li .file_menu_icon,.nav_down_list li .file_menu_icon,.nav_group .nav_btn_group .file_menu_icon,.selection_right_menu li .file_menu_icon{display:inline-block;background-image:url(../img/file_icon/file_menu_icon.png);background-size:auto!important;background-repeat:no-repeat;width:16px;height:16px;position:absolute;left:22px;top:50%;margin-top:-8px;font-style:normal}.selection_right_menu li .property_file_icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA7klEQVQ4T6WTQUrDUBCG///lBnZboeAFvEBvUcWV0BIy3dadJ3CnSzNJcF20d+giF/AALRTi1t7gZaQhkVJ0kb5ZDMyb+T+YNzPEiRVFceG9nwAYkxwf0mZWAiijKFrFcbw/lvA4yLLszsyeAIxOwW28I/mYJMmyy/8CVNX+Ef35LCKNtnGq+gxg0QcA4EVEHpjn+bCu608Ag56Ab+fcNdM0nZJ86yluys1sdgC8kpyfCUipqhsAVy3gQ0RuVbUCMCR5772vnHNrAF8icqmq7wBu2vptOCC4heBPDB5j8CJ14wta5Q4SdEwdpO85/wAdKopJjTYPqgAAAABJRU5ErkJggg==)!important}.nav_down_list:before{content:'';display:inline-block;width:100%;height:5px;position:absolute;top:-5px}.edit_file_icon,.open_file_icon,.open_find_dir_file_icon{background-position:-16px -16px}.copy_file_icon{background-position:0 -96px}.paste_file_icon{background-position:-16px -64px}.shear_file_icon{background-position:-16px -80px}.rename_file_icon{background-position:0 -64px}.del_file_icon{background-position:0 -80px}.download_file_icon{background-position:-16px -48px}.folad_file_icon,.killing_file_icon,.unzip_file_icon{background-position:0 -32px}.decompression_file_icon{background-position:0 -32px}.authority_file_icon,.cancel_share_file_icon,.dir_kill_file_icon,.share_file_icon{background-position:-17px -773px;background-image:none!important;width:17px!important;height:17px!important;font-size:19px;top:0!important;margin-top:0!important;color:#31dc48}.dir_kill_file_icon{font-size:17px;color:#c77f22}.authority_file_icon{color:#f2ce4d}.cancel_favorites_file_icon,.favorites_file_icon{background-position:0 -32px;background-position:-17px -773px;width:17px!important;height:17px!important;margin-top:-8.5px!important}.file_menu_down .glyphicon-triangle-right{font-size:8px;position:absolute;right:8px;top:8px;color:#555;transform:scale(.8)}.selection_right_menu.left_menu .file_menu_down .set_group{top:-6px;left:-210px}.file_menu_down .set_group{display:none;padding:5px 0;background:#fff;width:210px;border:1px solid #dadada;position:absolute;right:-210px;top:-6px;box-shadow:5px 5px 12px rgba(0,0,0,.15)}.file_right_menu li:hover .set_group{display:inline-block}.file_right_menu li .power_file_icon,.nav_group .nav_btn_group .power_file_icon{background-image:url(../img/file_icon/power_file_icon.png)!important;background-size:16px!important}.add_favorites_icon{background-position:-16px -773px}.refresh_file_icon{background-position:-16px -513px}.upload_file_icon{background-position:0 -48px}.create_dir_file_icon,.create_file_icon,.newly_file_icon{background-position:0 -16px}.compress_file_icon,.rar_file_icon,.tar_gz_file_icon,.zip_file_icon{background-position:-16px -32px}.paste_file_icon{background-position:-16px -64px}.total_icon_icon{background-position:0 -561px}.total_list_icon{background-position:-16px -625px}.selection_right_menu li .file_menu_icon.web_shell_file_icon{background-image:url(../img/file_icon/ico-cmd.png)}.onselectstart{position:fixed;display:none;width:0;height:0;background:#bbf1e3;border:1px dashed #a1d0c3;opacity:.5}.file_list_header .file_check.active,.file_list_header .file_check.active_2,.file_tr.active .file_check{background:#5fb878!important;border:1px solid #5fb878!important;color:#fff}.file_list_header .file_check.active_2::before{font-weight:700;content:"";height:2px;width:10px;display:inline-block;border-radius:1px;position:relative;background-color:#fff;top:-4px;left:2px}.file_list_header .file_check.active::before,.file_tr.active .file_check::before{font-weight:700;content:"\e75e";position:relative;top:-2px;font-size:14px;font-family:iconfont!important;font-style:normal;-webkit-font-smoothing:antialiased}.statistics{display:inline-block;height:30px;line-height:30px;padding:0 10px;position:absolute;border-left:4px solid #20a53a;bottom:38px;font-size:14px}.statistics span{margin:0 2px;font-size:14px}.bt_file_updated{position:fixed;width:100%;height:100%;left:0;top:0;right:0;bottom:0;background:rgba(32,165,58,.5);z-index:9999;display:none}.updated_title{text-align:center;position:absolute;z-index:99999;color:#fff;width:100%;font-size:50px;opacity:.8;top:50%}.updated_icon{position:absolute;top:50%;left:50%;margin-left:-125px;margin-top:-200px}.updated_icon .btn{width:250px;height:200px;position:relative;text-align:center;margin:15px;overflow:hidden;border-radius:100%;display:inline-block}.updated_icon .cloud{width:200px;height:80px;position:relative;background:#fff;display:inline-block;border-radius:50px;margin:40px auto 0}.updated_icon .cloud:after{content:'';position:absolute;width:120px;height:80px;border-radius:80px 80px 0 0;top:-40px;background:#fff;left:40px}.updated_icon .cloud:before{content:'';position:absolute;background:#dedede;border-radius:10px;width:90px;height:6px;z-index:2;left:0;right:0;bottom:25px;margin:auto}.updated_icon .file{position:absolute;left:0;right:0;margin:auto;z-index:5;top:50px;width:80px;height:100px;display:inline-block;overflow:hidden}.updated_icon .file:after{content:'';position:absolute;background:#5fb878;width:80px;height:80px;left:0;top:0;border-radius:0 0 10px 10px;-webkit-animation:uplaod 3s linear infinite;animation:uplaod 3s linear infinite}.updated_icon .file:before{content:'';background:#e2e3e3;color:#e2e3e3;width:60px;height:5px;position:absolute;left:0;right:0;top:20px;z-index:3;margin:auto;-webkit-animation:uplaod 3s linear infinite;animation:uplaod 3s linear infinite;box-shadow:0 15px,0 30px}@-webkit-keyframes uplaod{0%{-webkit-transform:translateY(0);transform:translateY(0)}100%{-webkit-transform:translateY(-100px);transform:translateY(-100px)}}@keyframes uplaod{0%{-ms-transform:translateY(0);-webkit-transform:translateY(0);transform:translateY(0)}100%{-ms-transform:translateY(-100px);-webkit-transform:translateY(-100px);transform:translateY(-100px)}}.file_nav_view{height:32px;margin:15px 0;font-size:0}.file_nav_view .nav_group{display:inline-block;vertical-align:top}.file_nav_view .nav_group.dividing{border-color:#fff;height:100%;width:1px;background:#cfcfcf;cursor:default;margin:0 10px}.nav_group .nav_btn:hover,.nav_group .nav_btn_group:hover{background-color:#20a53a;border-color:#20a53a}.nav_group .nav_btn:hover .nav_btn_title,.nav_group .nav_btn_group:hover .nav_btn_title{color:#fff}.nav_group .nav_btn:hover>.iconfont,.nav_group .nav_btn_group:hover>.iconfont{color:#fff}.file_nav_view .nav_btn_group.active:hover{border-color:#cfcfcf}.file_nav_view .nav_btn:hover .nav_down_list,.file_nav_view .nav_btn_group:hover .nav_down_list{display:block}.file_nav_view .nav_btn::before{content:'';display:none;width:100%;height:5px;position:absolute;bottom:-5px;left:0}.file_nav_view .nav_btn:hover::before{display:inline-block}.nav_group .nav_btn,.nav_group .nav_btn_group{padding:0 10px;display:inline-block;box-sizing:border-box}.terminal_view .nav_btn_title{vertical-align:middle!important;margin-left:5px}.terminal_view{padding:4px 10px!important}.terminal_view .iconfont{vertical-align:middle}.nav_group .nav_btn,.nav_group .nav_btn_group{display:inline-block;margin-right:5px;border:1px solid #cfcfcf;box-sizing:border-box;cursor:pointer;border-radius:2px;transition:all .1s;position:relative;padding:7px 10px}.nav_group{font-size:0}.nav_group .share_file_list{margin-right:8px}.nav_group .nav_btn_group{margin-right:0;vertical-align:bottom}.nav_group .nav_btn_group{padding-left:28px}.nav_group .nav_btn_group .file_menu_icon{left:7.5px}.nav_group .nav_btn .icon-xiala,.nav_group .nav_btn_group .icon-xiala{font-size:12px;margin-left:2px;vertical-align:middle}.nav_group .nav_btn .nav_btn_title,.nav_group .nav_btn_group .nav_btn_title{font-size:12px;color:#555;vertical-align:top}.nav_group .nav_btn_group{border-right-width:1px;border-left-width:0;border-top-right-radius:0;border-bottom-right-radius:0;border-top-left-radius:0;border-bottom-left-radius:0}.nav_group .nav_btn_group.active{background:#ececec}.nav_group .nav_btn_group:last-child{border-right-width:1px;border-top-right-radius:2px;border-bottom-right-radius:2px}.nav_group .nav_btn_group:first-child{border-left-width:1px;border-top-left-radius:2px;border-bottom-left-radius:2px}.nav_group .nav_btn.nav_btn_title .icon-terminal,.nav_group .nav_btn_group .nav_btn_title .icon-terminal{font-size:17px}.nav_group .nav_btn .icon-shangchuanxiazai,.nav_group .nav_btn_group .icon-shangchuanxiazai{font-size:12px;margin-right:5px;color:#555}.nav_group.mount_disk_list{padding-left:8px;border-left:1px solid #ececec}.nav_group.multi{padding-right:8px;border-right:1px solid #ececec}.nav_group.mount_disk_list>.glyphicon{font-size:13px;color:#666;position:relative;top:2px}.float_r.menu-header-foot{font-size:0}.nav_group.file_all_paste,.nav_group.recycle_bin{padding-left:8px}.nav_group.recycle_bin .nav_btn{margin-right:8px}.nav_group.mount_disk_list .nav_btn,.nav_group.recycle_bin .nav_btn{padding:7px 10px 7px 5px}.nav_group.mount_disk_list .nav_btn span,.nav_group.recycle_bin .nav_btn span{font-size:12px;margin-left:5px;color:#666}.file_disk_list .nav_btn span:nth-child(0){font-size:12px}.nav_group.recycle_bin .nav_btn .glyphicon{top:2px}.nav_group.mount_disk_list .nav_btn:hover span,.nav_group.recycle_bin .nav_btn:hover span{color:#fff!important}.nav_down_list{display:none;transition:all .5s;list-style:none;background:#fff;border-radius:2px;box-shadow:4px 2px 5px 0 #ececec;border:1px solid #cfcfcf;position:absolute;left:-1px;top:32px;z-index:999999;overflow:auto;max-height:300px;max-width:140px}.nav_group.mount_disk_list .disk_title_group_btn{display:none}.nav_group.mount_disk_list.thezoom .disk_title_group_btn{border-radius:2px;height:32px;line-height:32px;border:1px solid #cfcfcf;padding:7px 10px;display:inline-block;width:115px;box-sizing:border-box;cursor:pointer;position:relative;transition:all .5s}.nav_group.mount_disk_list .file_disk_list .nav_btn:last-child{margin-right:0!important}.nav_group.mount_disk_list.thezoom .file_disk_list{display:none}.nav_group.mount_disk_list.thezoom .disk_title_group_btn .disk_title_group{font-size:12px;color:#666;position:absolute;top:0}.nav_group.mount_disk_list.thezoom .disk_title_group_btn::before,.nav_group.multi.thezoom .batch_multi_title::before{content:'';display:none;width:100%;height:5px;position:absolute;bottom:-5px;left:0}.nav_group.mount_disk_list.thezoom .disk_title_group_btn:hover::before,.nav_group.multi.thezoom .batch_multi_title:hover::before{display:inline-block}.nav_group.mount_disk_list.thezoom .disk_title_group_btn:hover,.nav_group.multi.thezoom .batch_multi_title:hover{background-color:#20a53a;border-color:#20a53a}.nav_group.mount_disk_list.thezoom .disk_title_group_btn:hover .disk_title_group,.nav_group.mount_disk_list.thezoom .disk_title_group_btn:hover>i,.nav_group.multi.thezoom .batch_multi_title:hover>i,.nav_group.multi.thezoom .batch_multi_title:hover>span{color:#fff}.nav_group.mount_disk_list.thezoom .disk_title_group_btn>i{font-size:12px;margin-left:2px;position:absolute;right:9px;top:0}.nav_group.mount_disk_list.thezoom .disk_title_group_btn .nav_down_list{max-width:360px}.nav_group.mount_disk_list.thezoom .disk_title_group_btn .nav_down_list li i{font-size:13px;position:absolute;left:8px;top:8px;color:#666}.nav_group.mount_disk_list.thezoom .disk_title_group_btn .nav_down_list li span{width:100%;padding-right:10px}.nav_group.mount_disk_list.thezoom .disk_title_group_btn:hover ul.nav_down_list{display:block}.nav_group.multi.thezoom{padding-right:0;border-right:none}.nav_group.multi .batch_multi_title>i,.nav_group.multi .batch_multi_title>span{display:none}.nav_group.multi.thezoom .batch_multi_title>i,.nav_group.multi.thezoom .batch_multi_title>span{display:inline-block;font-size:12px;color:#666}.nav_group.multi.thezoom .batch_multi_title>i{font-size:12px;margin-left:2px;position:absolute;right:9px;top:7px}.nav_group.multi.thezoom{position:relative}.nav_group.multi.thezoom:hover .batch_group_list{display:block}.nav_group.multi.thezoom .batch_multi_title{border-radius:2px;height:32px;border:1px solid #cfcfcf;padding:7px 10px;display:inline-block;width:85px;box-sizing:border-box;cursor:pointer;position:relative}.nav_group.multi.thezoom .batch_group_list{display:none;width:85px;margin-top:12px;position:absolute;left:0;border:1px solid #cfcfcf;border-radius:2px;box-shadow:4px 2px 5px 0 #ececec;z-index:9999;background-color:#fff}.nav_group.multi.thezoom .batch_group_list .nav_btn_group{width:100%;border:none}.nav_group.multi.thezoom .batch_group_list .nav_btn_group:hover{background-color:#f0f0f0}.nav_group.multi.thezoom .batch_group_list .nav_btn_group:hover span{color:#666}.selection_right_menu li .file_menu_icon.create_files_file_icon{height:17px;background-image:url(../img/file_icon/file_text2.png)}.file_nav_view .nav_down_list li i.file_new_icon{width:17px;height:17px;position:absolute;top:7.5px;left:5px;background-repeat:no-repeat;background-size:cover;background-position:center;image-rendering:-webkit-optimize-contrast;background-image:url(../img/file_icon/file_text2.png)}.file_nav_view .nav_down_list li i.file_new_folder_icon{width:19px;height:17px;position:absolute;top:7.5px;left:4px;background-repeat:no-repeat;background-size:cover;background-position:center;image-rendering:-webkit-optimize-contrast;background-image:url(../img/file_icon/folder.png)}.file_list_content .file_tr textarea:focus{outline:0!important;border:1px solid #ccc}.file_table_view .file_list_content .file_tr.editr_tr textarea,.file_table_view.icon_view .createModel textarea{width:70px;padding:5px 2px;border-color:#ccc;overflow:hidden;resize:none}.file_table_view .file_list_content .file_tr.editr_tr input,.file_table_view.list_view .createModel input{width:100%;padding:0 0 0 5px;height:25px;border:1px solid #ececec;outline:0;vertical-align:middle;line-height:25px}.favorites_file_path .nav_down_list{max-width:230px}.favorites_file_path .nav_down_list li span{width:230px!important;padding-right:20px}.favorites_body .favorites_span{width:680px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.favorites_file_path .nav_down_list li span.icon-shezhi1{left:6px}.nav_down_list li{height:30px;line-height:30px;position:relative;box-sizing:border-box;overflow:hidden;color:#555}.nav_down_list li:last-child{border:none}.nav_down_list li:hover{background:#f0f0f0;color:#555}.file_dir_item .nav_down_list li span{line-height:30px!important}.file_dir_item .file_dir span{color:#666}.nav_down_list li span:nth-child(1){font-size:14px;position:absolute;line-height:28px;top:0;left:12px;color:#666}.nav_down_list li span:nth-child(2){display:inline-block;font-size:12px;color:#555;line-height:30px;padding-left:28px;vertical-align:top;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:140px}.nav_down_list li .iconfont{color:#aaa;font-size:19px;position:absolute;left:5px}.nav_down_list li .file_menu_icon{left:5px;margin-top:-8.5px}.nav_down_list li .icon-yingpan{font-size:15px}.nav_group.btn-group-sm>.btn.cut_view_model{padding:6px 10px}.file_path_views{height:30px;border-bottom:1px solid #eee;font-size:0;box-sizing:content-box;padding-bottom:15px}.file_path_views .file_path_upper{display:inline-block;height:30px;line-height:30px;vertical-align:top;border:1px solid #cfcfcf;box-sizing:border-box;text-align:center;vertical-align:top;border-top-left-radius:2px;border-bottom-left-radius:2px;border-right:none;color:#666}.file_path_views .file_path_upper{padding:0 12px}.file_path_views .file_path_upper:hover{background-color:#20a53a;color:#fff;cursor:pointer;border:1px solid #20a53a;border-right:none}.file_path_views .file_path_upper:hover i{background-position:-60px 6px}.file_path_views .file_path_upper span{font-size:12px;display:inline-block;height:28px;line-height:28px;vertical-align:top;top:0}.file_path_views .forward_path{display:inline-block;height:30px;line-height:30px;vertical-align:top;margin-right:-1px}.file_path_views .forward_path>span{display:inline-block;text-align:center;height:30px;line-height:30px;width:auto;padding:0 9px;border:1px solid #cfcfcf;box-sizing:border-box;border-radius:2px;cursor:pointer}.file_path_views .forward_path>span span{font-size:15px}.file_path_views .forward_path span:hover{background:#20a53a;border:1px solid #20a53a}.file_path_views .forward_path span:hover:first-child i{background-position:-90px 6px}.file_path_views .forward_path span:hover:last-child i{background-position:-75px 6px}.file_path_views .forward_path span.active:hover{background:#efefef;box-shadow:inset 0 3px 6px rgba(0,0,0,.1);border-color:#cfcfcf}.file_path_views .forward_path span:active{box-shadow:inset 0 3px 6px rgba(0,0,0,.1)}.file_path_views .forward_path span.active{background:#efefef;box-shadow:inset 0 3px 6px rgba(0,0,0,.1);cursor:no-drop;transition:all .5s}.file_path_views .forward_path span:first-child{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.file_path_views .forward_path span:last-child{border-left:1px solid #cfcfcf;border-radius:0}.file_path_input{display:inline-block;height:30px;width:50%;box-sizing:border-box;position:relative}.file_path_input input{border:1px solid #cfcfcf;display:inline-block;width:100%;position:absolute;top:0;left:0;height:30px;padding-left:5px;box-sizing:border-box;background:#f3f3f3;font-size:13px}.file_path_input input.focus{background-color:#fff}.file_dir_omit{display:inline-block;font-size:0;height:30px;line-height:28px;position:relative;left:0;width:45px;cursor:pointer;border:1px solid transparent}.file_dir_omit.active{background-color:#f0f9f7;border-top:1px solid #cfcfcf;border-bottom:1px solid #cfcfcf;border-right:1px solid #ececec}.file_dir_omit.active .iconfont{background-color:#f0f9f7;border-left:#dadada;border-right:#dadada}.file_dir_icon,.file_dir_omit>span{display:inline-block;font-size:14px;background-image:url(../img/file_icon/file_menu_icon.png);background-position:0 -16px;background-size:auto;height:16px;position:absolute;top:6px;width:16px;left:5px;z-index:99}.file_dir_icon{left:8px;top:7px}.file_dir_omit .iconfont{position:absolute;top:0;right:0;font-size:12px!important;padding:0 2px}.file_dir_omit:hover{background:#f3fbf9;border-top:1px solid #cfcfcf;border-bottom:1px solid #cfcfcf;border-right:1px solid #ececec}.file_dir_omit:hover .iconfont{background:#f3fbf9;border-left:1px solid #ececec}.file_dir_omit .nav_down_list{overflow:hidden}.file_dir_omit .nav_down_list .file_dir_item{display:block;height:32px;position:relative;box-sizing:border-box}.file_dir_omit .nav_down_list .file_dir_item .file_dir{width:140px;background:#fff;height:32px;line-height:32px;padding-left:28px}.file_dir_omit .nav_down_list .file_dir_item .iconfont,.file_dir_omit .nav_down_list .file_dir_item .nav_down_list{display:none}.file_dir_item{display:inline-block;font-size:0;height:28px;line-height:28px;cursor:pointer;color:#777;box-sizing:border-box;position:relative;top:1px!important}.file_dir_item.active .nav_down_list{display:block;top:30px}.file_dir_view{position:relative;font-size:0;z-index:99;display:inline-block}.file_dir_view .file_dir_item.active{background:#f3fbf9;border:1px solid #d1e8e0;box-sizing:border-box;margin-top:-1px;box-shadow:inset 0 0 2px rgba(0,0,0,.1)}.file_dir_view .file_dir_item.active .iconfont{border-left:1px solid #d1e8e0}.file_dir_item .iconfont,.file_dir_omit .iconfont{vertical-align:top;font-size:14px;font-weight:600;height:28px;display:inline-block;border-left:1px solid #f3f3f3;border-right:1px solid #f3f3f3;transition:all .5s;background:#f3f3f3}.file_dir_item .file_dir{display:inline-block;padding:0 5px;font-size:12.5px;position:relative;height:28px;line-height:29px;color:#555;background:#f3f3f3;border-left:1px solid transparent;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file_dir_item .nav_down_list li{height:30px;line-height:30px;background:#f9f9f9}.file_dir_item .nav_down_list li.active{font-weight:600;color:#333;background-color:#eee}.file_dir_item .nav_down_list li:hover{background:#eee}.file_dir_item:first-child .file_dir{position:relative;top:0;font-size:12.4px;padding-left:10px}.file_dir_item:first-child{left:-1px}.file_dir_item:first-child .file_dir span:first-child{vertical-align:text-top}.file_dir_view .file_dir_item:hover{background:#f3fbf9;box-shadow:inset 0 0 2px rgba(0,0,0,.1)}.file_dir_view .file_dir_item:hover .file_dir{background:#f3fbf9;border-left:1px solid #ececec}.file_dir_view .file_dir_item:hover .iconfont{border-left:1px solid #ececec;border-right:1px solid #ececec;background:#f3fbf9}.file_path_refresh{display:inline-block;width:30px;height:30px;line-height:30px;border:1px solid #cfcfcf;box-sizing:border-box;text-align:center;vertical-align:top;border-left:0;border-top-right-radius:2px;border-bottom-right-radius:2px;cursor:pointer}.file_path_refresh:hover i{background-position:-105px 6px}.file_path_collect:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.file_path_collect i{font-size:14px;position:relative;top:-1px}.file_path_collect .icon-wujiaoxing{color:#ffca2e}.file_path_collect:hover{background:#20a53a;border-color:#38983b;color:#fff}.search_path_views{border:1px solid #20a53a;display:inline-block;vertical-align:top;float:right;font-size:0;position:relative}.search_path_views .set_updown{height:30px;line-height:30px;position:absolute}.file_path_views .forward_path i{top:0}.search_path_views .file_search_input{border:none;display:inline-block;height:28px;box-sizing:border-box;width:250px;padding-left:8px;box-sizing:border-box;outline:0;border-right:none;font-size:12px}.search_path_views .is_search_children{margin:0 5px;color:#cfcfcf;font-size:13px}.search_path_views .is_search_children:hover{color:#20a53a;cursor:pointer}.search_path_views .path_btn{display:inline-block;height:28px;background:#20a53a;vertical-align:top;color:#fff;border:none;width:35px;text-align:center;cursor:pointer;outline:0}.search_path_views .path_btn:hover{background-color:#38983b}.search_path_views .path_btn:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.search_path_views .set_up{display:none;position:absolute;top:30px;left:0;width:210px;height:30px;background:#fff;box-shadow:4px 2px 5px 0 #ececec;z-index:9999;border:1px solid #ececec;border-radius:2px}.search_path_views .file_search_config{position:absolute;right:35px;font-size:12px;color:#666;width:105px;border-top:none;background:#fff;height:28px;line-height:28px;top:0}.search_path_views .file_search_config .file_search_checked{position:relative;height:15px;width:15px;border-radius:1px;cursor:pointer;text-align:center;margin:0;vertical-align:sub;border:1px solid #aaa;border-radius:2px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased}.search_path_views .file_search_config .file_search_checked.active{background:#20a53a;color:#fff;border-color:#20a53a}.search_path_views .file_search_config .file_search_checked.active:after{content:"\e013";font-size:12px;transform:scale(.85);position:absolute;left:.75px;top:.75px}.search_path_views .file_search_config label{position:absolute;right:3px;cursor:pointer;top:0;font-weight:400}.mr0{margin-right:0!important}.ml0{margin-left:0!important}.plr25{padding:0 25px}.float_r{float:right}.file_bodys{background-color:#fff;margin:10px 15px;padding:15px 20px;font-size:12px;-webkit-user-select:none;-moz-user-select:none;-o-user-select:none;user-select:none}.file_list_icon{border-top:1px solid #eee;padding-top:15px}.file_list_icon .file_table_view.list_view .file_list_content .file_tr{display:inline-block;width:auto;background:#fff;border-color:#fff;position:relative;height:auto;margin:2.5px 5px;cursor:pointer;vertical-align:top}.file_table_view.list_view .file_list_content .file_tr.selected{background:#f0f9f7!important}.file_list_icon .file_table_view.list_view .file_list_content .file_tr .file_td{height:auto;padding:0;line-height:normal}.file_list_icon .file_table_view.list_view .file_list_content .file_tr .file_td:not(.file_name){display:none}.file_list_icon .file_table_view.list_view .file_list_content .file_tr .file_td:nth-of-type(1){position:absolute;left:0;top:0}.file_list_icon .file_table_view.list_view .file_list_content .file_tr .file_td:nth-of-type(2){width:99px;min-width:99px}.file_list_icon .file_table_view.list_view .file_list_content .file_type{width:100%;height:65px}.file_list_icon .file_table_view.list_view .file_list_content .file_icon{top:15px;width:50px;height:50px;left:50%;margin-left:-25px;background-size:auto}.file_list_icon .file_title{display:inline-block;text-align:center;color:#666;padding:10px 5px 15px 5px;font-size:12px;word-break:break-all;white-space:initial}[data-type=folder] .file_title{cursor:pointer}.file_tips{margin-left:30px;color:#999;font-size:12px}.file_bodys{margin-bottom:57px;-webkit-box-sizing:initial;-moz-box-sizing:initial;box-sizing:initial}.file_path_input{position:relative}.file_path_input .file_dir_view{white-space:nowrap;position:absolute;left:1px;height:30px;line-height:28px}.not-btn-border .layui-layer-btn{background-color:#fff;border-top:none}.not-btn-border .layui-layer-btn a{line-height:30px;padding:0 15px}.not-btn-border .layui-layer-btn0{margin-left:15px;border-radius:2px;padding:0 15px}.not-btn-border .layui-layer-btn1{border-radius:2px}.not-btn-border .layui-layer-btn{padding:9px 15px 10px}.file_upload_view{padding:10px 15px 0 15px}.file_select_file{padding-bottom:10px}.file_select_file button{padding:7.5px 12px;border-radius:2px;border:none;background-color:#4592f0;border-color:#367fa9;margin-right:15px;color:#fff;outline:0}.file_select_file button:hover{background-color:#4188de;border-color:#367fa9}.file_select_file button:active{box-shadow:inset 0 3px 6px rgba(0,0,0,.2)}.file_content_list{width:100%;height:355px;background-color:#fafbfe;border-radius:2px;border:#e4e5e9 1px solid;position:relative}.file_upload_tips{text-align:center;color:#ccc;font-size:28px;position:absolute;width:100%;height:40px;line-height:40px;top:50%;margin-top:-40px}.upload_or_download .glyphicon{color:#666}.upload_or_download_view{padding:10px 20px}.upload_btn_group{margin-top:15px;display:inline-block;text-align:right;width:100%}.upload_btn_group button{margin-right:15px}.upload_btn_group button:last-child{margin:auto}.filePage{width:100%;height:auto}.filePage>div{margin-top:10px}.filePage>.page_num{float:left;line-height:30px;padding:0 5px}.filePage>.page_num a{float:initial;border:none;line-height:30px;color:#20a53a;display:inline;font-size:13px;padding:0 5px}.filePage>.page_num a:hover{background:#fff;color:#20a53a;text-shadow:0 1px 0 #fff;cursor:pointer}.filePage.showRow{margin:0 3px;border:#cfcfcf 1px solid}.filePage .page span{height:30px;line-height:30px;padding:0 12px}.filePage .Pcount-item{border-left:none;border-right:1px solid #ececec;color:#333}.filePage .Pcount-item .showRow{margin:0 3px;border:1px solid #eee;border-radius:2px}.preview_images_mask{position:fixed;left:50%;top:50%;background:rgb(0 0 0 /.85);z-index:9999;height:650px;width:750px;margin-left:-375px;margin-top:-325px;overflow:hidden;-webkit-box-shadow:0 0 3px 1px rgba(0,0,0,.3);-moz-box-shadow:0 0 3px 1px rgba(0,0,0,.3);box-shadow:0 0 3px 1px rgba(0,0,0,.3);border-radius:4px}.preview_images_mask .preview_head{height:45px;line-height:45px;width:100%;position:absolute;z-index:999}.preview_images_mask .preview_title{text-align:center;width:100%;display:inline-block;font-size:16px;background-color:rgb(0 0 0 /.45);color:#cacaca;cursor:all-scroll}.preview_images_mask .preview_close,.preview_images_mask .preview_full,.preview_images_mask .preview_small{position:absolute;right:0;width:45px;height:45px;line-height:47px;font-size:19px;text-align:center;color:#d6d6d6}.preview_images_mask .preview_full,.preview_images_mask .preview_small{right:45px;font-size:18px}.preview_images_mask .preview_close:hover,.preview_images_mask .preview_full:hover,.preview_images_mask .preview_small:hover{background-color:#444;color:#fff;cursor:pointer}.preview_images_mask .preview_body{position:relative;height:100%;z-index:99;padding:45px 0}.preview_images_mask:hover .preview_cut_view{opacity:1}.preview_images_mask:hover .preview_toolbar{opacity:1}.preview_images_mask .preview_body img{position:absolute;display:none;transition:all .2s;cursor:all-scroll;display:inline-block;opacity:0;webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.preview_images_mask .preview_toolbar{position:absolute;bottom:0;width:250px;left:50%;margin-left:-125px;height:45px;border-radius:3px;background-color:rgb(0 0 0 /.65);font-size:18px;z-index:999;opacity:0;transition:all 1s}.preview_images_mask .preview_toolbar a{width:50px;height:45px;line-height:45px;text-align:center;display:inline-block;transition:all .5s;border-radius:3px}.preview_images_mask .preview_toolbar a:hover{background-color:rgb(86 86 86 / 75%)}.preview_images_mask .preview_toolbar span{vertical-align:middle;color:#bbb}.preview_images_mask .preview_toolbar span.reverse-repeat{transform:rotateY(180deg)}.preview_images_mask .preview_cut_view{opacity:0;transition:all .5s}.preview_images_mask .preview_cut_view a:hover{background-color:#3a3a3a;color:#fff}.preview_images_mask .preview_cut_view a{position:absolute;font-size:26px;width:50px;height:50px;text-align:center;line-height:50px;top:50%;margin-top:-25px;background-color:rgb(194 194 194 / 80%);border-radius:50%;transition:all .2s;z-index:999}.preview_images_mask .preview_cut_view a:nth-child(1){left:3%}.preview_images_mask .preview_cut_view a:nth-child(2){right:3%}.preview_images_mask .preview_cut_view span{vertical-align:top;height:50px;width:50px;text-align:center;line-height:50px}.exists_files_style{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block;width:145px}.readonly-form-input:focus{outline:0;background-color:-internal-light-dark(rgba(239,239,239,.4),rgba(59,59,59,.4));cursor:default}.readonly-form-input:active{border-color:#ccc;box-shadow:none;-webkit-box-shadow:none}.icon-file-upper{background-position:0 6px}.icon-file-back{background-position:-30px 6px}.icon-file-forward{background-position:-15px 6px}.icon-file-refresh{background-position:-45px 6px}.icon-file{display:inline-block;background-image:url(../images/iconfont-file.png);height:28px;line-height:28px;width:15px;background-repeat:no-repeat}.bt-warp{min-width:1400px}.file_menu_tips{display:inline-block;font-size:14px;line-height:32px}.file_menu_tips span{vertical-align:middle;margin-right:3px;color:#f0ad4e}.file_menu_tips span:nth-child(2){font-size:12px}.file_path_collect:hover,.file_path_refresh:hover{background:#20a53a;border-color:#38983b;color:#fff}.bt-property-setting .tab-nav{font-size:0}.bt-property-setting .tab-nav span{font-size:12px}.bt-property-setting .tab-con{padding:5px 0 0}.bt-property-setting .property-box{display:none}.bt-property-setting .property-box.active{display:inline-block;width:100%}.bt-property-setting .property-box .attr-box{margin:5px 0;width:100%}.bt-property-setting .property-box .attr-name{width:120px;padding-left:10px;float:left}.bt-property-setting .property-box .attr-content{margin-left:120px}.bt-property-setting .property-box .attr-content input{height:32px;line-height:32px;border:1px solid#ccc;outline:0;padding-left:5px;width:350px;border-radius:2px}.bt-property-setting .property-box .attr-content input:focus{border-color:#20a53a}.bt-property-setting .property-box .file_icon{height:50px;width:50px;position:relative;display:inline-block;left:-10px;top:2px}.bt-property-setting .property-box .dividing{width:100%;height:1px;margin:10px 0;background-color:#ececec}.bt-property-setting .property-box .attr-box .attr-content,.bt-property-setting .property-box .attr-box .attr-name{height:25px;line-height:25px}.bt-property-setting .property-box .attr-box .attr-content span{width:450px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.details_box_view,.history_box_view{border:1px solid #ececec}.details_box_view,.history_box_view,.property-box{height:388px!important;overflow:auto}.details_box_view table,.history_box_view table{width:100%}.details_box_view thead tr,.history_box_view thead tr{background-color:#f6f6f6;color:#555;font-weight:400}.details_box_view tbody tr:hover,.history_box_view tbody tr:hover{background:#f0f9f7;cursor:pointer}.details_box_view td div,.details_box_view th div,.history_box_view td div,.history_box_view th div{padding:5px;height:30px;line-height:20px}.details_box_view thead,.history_box_view thead{border-bottom:1px solid #f6f6f6}.ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block} \ No newline at end of file +.webDelete .vcode #vcodeResult{width:50px;height:26px}*{margin:0;padding:0}.file_table_view.list_view::-webkit-scrollbar{width:12px;height:7px;padding:2px;position:relative}.file_table_view.list_view::-webkit-scrollbar-thumb{border-radius:3px;box-shadow:inset 0 0 6px rgba(0,0,0,.1);background:#bbb}.file_table_view.list_view::-webkit-scrollbar-track{border-radius:3px;box-shadow:inset 0 0 6px rgba(0,0,0,.1);background:#ededed}.file_list_header{height:40px;background:#f6f6f6;border-bottom:none;position:relative;z-index:999;overflow-y:hidden}.file_list_header .file_checkbox,.file_table_view.list_view .file_list_content .file_checkbox{padding:12px!important;margin-left:0!important;cursor:default}.file_list_header .file_checkbox input,.file_table_view.list_view .file_list_content .file_checkbox input{display:none}.file_list_header .file_check,.file_table_view.list_view .file_list_content .file_check{width:16px;height:16px;line-height:16px;border:1px solid #d2d2d2;box-sizing:border-box;border-radius:2px;cursor:pointer;transition:all .2s;background-color:#fff}.file_list_header .file_main_title{line-height:40px;height:40px;overflow:hidden;border:1px solid transparent;border-bottom:none}.file_list_header .file_check i,.file_table_view.list_view .file_list_content .file_check i{position:relative;top:-2px;font-size:14px}.file_table_view.icon_view{position:relative;overflow:hidden}.file_table_view.list_view{position:relative;overflow:auto;z-index:1}.file_table_view .file_name .file_title i:hover{color:#000;cursor:pointer}.file_table_view.list_view .file_name{min-width:120px}.file_table_view.icon_view .file_list_header{display:none}.file_table_view.icon_view .app_menu_group,.file_table_view.icon_view .file_accept,.file_table_view.icon_view .file_checkbox,.file_table_view.icon_view .file_mtime,.file_table_view.icon_view .file_name .iconfont,.file_table_view.icon_view .file_ps,.file_table_view.icon_view .file_size,.file_table_view.icon_view .file_type,.file_table_view.icon_view .file_user{display:none}.file_table_view.icon_view .file_tr{display:inline-block;width:80px!important;min-height:90px;padding:5px;vertical-align:top;margin:0 10px 10px 0}.file_table_view.icon_view .file_name{width:100%!important;position:relative}.file_table_view.icon_view .file_ico_type{height:60px}.file_table_view.icon_view .file_list_content .file_icon{width:45px;height:45px;left:21%}.file_table_view.icon_view .file_name i{font-style:inherit}.file_table_view.icon_view .file_title.file_dir_status,.file_table_view.icon_view .file_title.file_file_status{width:70px;text-align:center;word-break:break-all;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden}.file_table_view.icon_view .file_list_content .file_tr:hover{background:#f0f9f7!important}.file_table_view.icon_view .file_list_content .file_tr.active{background:#f0f9f7!important;border:1px solid #ddefeb;box-sizing:border-box;padding:4px}.file_list_header .file_th{float:left;padding-left:10px;padding-right:10px;height:40px;line-height:40px;font-size:13px;color:#333;position:relative;cursor:pointer;margin-left:-5px}.file_list_header .file_th>span{vertical-align:middle}.file_table_view.list_view .file_type{min-width:80px;max-width:300px}.file_list_header .file_th{color:#333}.file_list_header .file_th.active{background:#eee;color:#555}.file_list_header .file_th:hover{background:#eee}.file_list_header .file_checkbox:hover,.file_list_header .file_operation:hover{background:#f3f3f3}.file_ps_title .set_file_ps{height:25px;line-height:25px;border:1px solid transparent;width:100%;border-radius:2px;padding-left:5px;outline:0}.file_ps_title .set_file_ps:hover{border:1px solid #20a53a!important;background-color:#fff!important}.file_list_header .file_width_resize{float:left;width:10px;margin-left:-5px;height:40px;overflow:hidden;position:relative;z-index:998}.file_list_header .file_width_resize::after{content:'';display:block;background:#e2e2e2;width:1px;height:25px;position:absolute;top:50%;left:50%;margin-left:-1px;margin-top:-12.5px}.file_list_header .file_name{margin-left:0}.file_operation.file_td .set_operation_group{display:none}.file_table_view.list_view .file_tr.active .set_operation_group.is_mobile{display:block!important}.file_list_header .icon_sort{display:inline-block;height:40px;line-height:40px;transition:all .5s;float:right}.file_list_header .icon_sort.active{transform:rotate(180deg)}.file_list_header .icon_sort .iconfont{vertical-align:bottom}.file_table_view.list_view .file_list_shadow{width:100%;height:8px;position:absolute;left:0;right:0;background:-webkit-linear-gradient(top,rgba(220,220,220,.5),rgba(255,255,255,0));z-index:99}.file_table_view.list_view .file_shadow_bottom{background:-webkit-linear-gradient(top,rgba(255,255,255,0),rgba(220,220,220,.5));bottom:0}.align-center{text-align:center}.align-left{text-align:left}.align-right{text-align:right}.btlink{color:#20a53a;text-decoration:none}.file_table_view.list_view .file_list_content{min-width:750px;overflow:auto;border-top:1px solid #ececec}.file_table_view.list_view .file_list_content .file_tr{height:45px;box-sizing:border-box;color:#fff;border-bottom:1px solid #f3f3f3;border-left:1px solid transparent;border-right:1px solid transparent;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file_table_view.list_view .file_list_content .file_tr.editr_tr .file_td{display:none}.file_table_view.list_view .file_list_content .file_tr.editr_tr .file_name{display:inline-block;margin-left:40px}.file_table_view.list_view .file_name .iconfont{display:block}.file_table_view.list_view .file_list_content .file_tr.editr_tr .file_name .iconfont{display:none}.file_table_view.list_view .file_name .icon-favorites,.file_table_view.list_view .file_name .icon-share1{font-size:18px;width:18px;height:18px;vertical-align:middle;position:absolute;top:50%;right:5px;margin-top:-9px;color:#2e973e;display:inline-block}.file_table_view.list_view .file_name .icon-favorites{color:#f5a623;font-size:17px}.file_table_view.list_view .file_name .icon-favorites:before,.file_table_view.list_view .file_name .icon-share1:before{position:relative;top:-16px;height:18px;width:18px;cursor:pointer}.file_table_view.list_view .file_list_content .file_tr.editr_tr .rename_file_input{height:32px;line-height:30px;border:1px solid #cfcfcf;border-radius:2px;padding-left:5px;margin-right:15px;width:150px}.file_table_view.list_view .file_list_content .file_tr.editr_tr .rename_file_input:focus{outline:0;border:1px solid #20a53a}.file_table_view.list_view .file_list_content .file_tr.editr_tr .add_file_btn,.file_table_view.list_view .file_list_content .file_tr.editr_tr .clear_file_btn{height:32px;line-height:30px;padding:0 18px;margin-right:10px;background-color:#20a53a;border-color:#20a53a;border-radius:2px;color:#fff;border:1px solid transparent}.file_table_view.list_view .file_list_content .file_tr.editr_tr .clear_file_btn{color:#555;background-color:#fff;border-color:#ccc}.file_table_view.list_view .file_list_content .file_tr.editr_tr .add_file_btn::focus,.file_table_view.list_view .file_list_content .file_tr.editr_tr .clear_file_btn::focus{outline:0}.file_table_view.list_view .file_list_content .file_tr.editr_tr .add_file_btn:hover{color:#fff;background-color:#10952a;border-color:#398439}.file_table_view.list_view .file_ps .foo_menu{display:none}.file_table_view.list_view .file_tr:hover .foo_menu{display:inline-block;position:absolute;right:10px}.file_table_view.list_view .file_tr .foo_menu_title{position:relative;padding-right:10px}.file_table_view.list_view .file_tr .foo_menu_title i{display:inline-block;width:7px;height:7px;color:#20a53a;border-left:1px solid;border-bottom:1px solid;transform:rotate(-45deg);position:absolute;top:3px;right:0}.file_table_view.list_view .file_tr.active .foo_menu{display:inline-block;position:absolute;right:10px}.file_table_view.list_view .file_tr.active .foo_menu .foo_menu_title,.file_table_view.list_view .file_tr:hover .foo_menu .foo_menu_title{border:1px solid #20a53a;color:#20a53a!important;background:#fff;border-radius:2px;padding:4px 18px 4px 8px;font-size:12px;cursor:pointer;position:relative}.file_table_view.list_view .file_tr .foo_menu .foo_menu_title:hover,.file_table_view.list_view .file_tr.active .foo_menu .foo_menu_title:hover{border:1px solid #20a53a;color:#fff!important;background:#20a53a}.file_table_view.list_view .file_tr .foo_menu.foo_menu_click .foo_menu_title,.file_table_view.list_view .file_tr.active .foo_menu .foo_menu_title:hover,.file_table_view.list_view .file_tr:hover .foo_menu .foo_menu_title:hover{border:1px solid #20a53a;color:#fff!important;background:#20a53a}.file_table_view.list_view .file_tr .foo_menu.foo_menu_click .foo_menu_title::after,.file_table_view.list_view .file_tr.active .foo_menu .foo_menu_title:hover::after,.file_table_view.list_view .file_tr:hover .foo_menu .foo_menu_title:hover::after{color:#fff}.app_menu_group ul{color:#20a53a;height:50px;line-height:50px;display:inline-block;margin-left:8px}.app_menu_group ul li{display:inline-block;margin-right:10px}.app_menu_group ul li i,.app_menu_group ul li.separate,.file_table_view.list_view .file_list_content .file_tr.app_menu_operation .file_td:nth-child(n+3){display:none}.file_table_view.list_view .file_list_content .file_tr:last-child{border-bottom:1px solid #ececec}.file_table_view.list_view .file_list_content .file_tr.active,.file_table_view.list_view .file_list_content .file_tr:hover{background:#f0f9f7!important;border:1px solid #ddefeb;border-top:none}.file_table_view.list_view .file_list_content .file_tr:hover .set_operation_group{display:block}.file_table_view.list_view .file_list_content .file_tr.active .set_file_ps,.file_table_view.list_view .file_list_content .file_tr:hover .set_file_ps{border:1px solid transparent;background-color:#eff9f7}.file_table_view.list_view .file_list_content .file_td{height:45px;line-height:45px;padding:0 8px;color:#777;overflow:hidden;position:relative;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block;vertical-align:top}.file_table_view.list_view .file_list_content .file_td.file_operation{text-overflow:clip;text-align:right}.file_table_view.list_view .file_list_content .file_checkbox{padding:14px 12px!important}.file_table_view.list_view .file_list_content .file_ico_type{height:40px;line-height:40px;display:inline-block;position:relative}.file_table_view.list_view .file_title{vertical-align:top;padding-left:38px;width:100%;box-sizing:border-box;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.file_table_view.list_view .file_title.file_file_status i{font-style:normal}.file_table_view.list_view .file_title i{font-style:normal;cursor:pointer;color:#555}.file_table_view.list_view .file_title i:hover{color:#20a53a}.file_edit_input{height:31px;display:inline-block;width:100%;padding-left:5px;outline:0;border:1px solid #ebf3f1;margin-left:-5px;border-radius:2px}.file_list_content .file_icon{width:100%;width:30px;height:30px;position:absolute;top:7.5px;background-repeat:no-repeat;background-size:cover;background-position:center;image-rendering:-webkit-optimize-contrast;background-image:url(../img/file_icon/file.png);cursor:pointer}.file_list_content .file_html{background-image:url(../img/file_icon/html.png)}.file_list_content .file_css{background-image:url(../img/file_icon/css.png)}.file_list_content .file_js{background-image:url(../img/file_icon/javascript.png)}.file_list_content .file_json{background-image:url(../img/file_icon/json.png)}.file_list_content .file_folder{background-image:url(../img/file_icon/folder.png)}.file_list_content .file_php{background-image:url(../img/file_icon/php.png)}.file_list_content .file_log{background-image:url(../img/file_icon/log.png)}.file_list_content .file_txt{background-image:url(../img/file_icon/file.png)}.file_list_content .file_py{background-image:url(../img/file_icon/python.png);width:30px;height:30px;left:2.5px;top:10px}.file_list_content .file_png{background-image:url(../img/file_icon/png.png)}.file_list_content .file_lua{background-image:url(../img/file_icon/lua.png);width:30px;height:30px;left:2.5px;top:10px}.file_list_content .file_access{background-image:url(../img/file_icon/access.png)}.file_list_content .file_apk{background-image:url(../img/file_icon/apk.png)}.file_list_content .file_avi{background-image:url(../img/file_icon/avi.png)}.file_list_content .file_bmp{background-image:url(../img/file_icon/bmp.png)}.file_list_content .file_cdr{background-image:url(../img/file_icon/cdr.png)}.file_list_content .file_gif{background-image:url(../img/file_icon/gif.png)}.file_list_content .file_ico{background-image:url(../img/file_icon/ico.png)}.file_list_content .file_java{background-image:url(../img/file_icon/java.png)}.file_list_content .file_jpeg{background-image:url(../img/file_icon/jpeg.png)}.file_list_content .file_jpg{background-image:url(../img/file_icon/jpg.png)}.file_list_content .file_mkv{background-image:url(../img/file_icon/mkv.png)}.file_list_content .file_mov{background-image:url(../img/file_icon/mov.png)}.file_list_content .file_mp4{background-image:url(../img/file_icon/mp4.png)}.file_list_content .file_mpeg{background-image:url(../img/file_icon/mpeg.png)}.file_list_content .file_mpg{background-image:url(../img/file_icon/mpg.png)}.file_list_content .file_pdf{background-image:url(../img/file_icon/pdf.png)}.file_list_content .file_ppt,.file_list_content .file_pptx{background-image:url(../img/file_icon/ppt.png);width:30px;height:30px;left:2.5px}.file_list_content .file_rar{background-image:url(../img/file_icon/rar.png)}.file_list_content .file_rm{background-image:url(../img/file_icon/rm.png)}.file_list_content .file_rmvb{background-image:url(../img/file_icon/rmvb.png)}.file_list_content .file_sql{background-image:url(../img/file_icon/sql.png)}.file_list_content .file_swf{background-image:url(../img/file_icon/swf.png)}.file_list_content .file_tar{background-image:url(../img/file_icon/tar.png)}.file_list_content .file_webm{background-image:url(../img/file_icon/webm.png)}.file_list_content .file_webp{background-image:url(../img/file_icon/webp.png)}.file_list_content .file_wma{background-image:url(../img/file_icon/wma.png)}.file_list_content .file_wmv{background-image:url(../img/file_icon/wmv.png)}.file_list_content .file_doc,.file_list_content .file_docx{background-image:url(../img/file_icon/doc.png);width:30px;height:30px;left:2.5px}.file_list_content .file_xls,.file_list_content .file_xlsx{background-image:url(../img/file_icon/xls.png);width:30px;height:30px;left:2.5px}.file_list_content .file_xml{background-image:url(../img/file_icon/xml.png)}.file_list_content .file_zip{background-image:url(../img/file_icon/zip.png)}.file_list_content .file_gz,.file_list_content .file_tar{background-image:url(../img/file_icon/tar.png)}.content_right_menu,.file_right_menu,.selection_right_menu{padding:5px 0;width:190px;border:1px solid #dadada;box-shadow:5px 5px 12px rgba(0,0,0,.15);position:fixed;left:-100%;top:-100%;z-index:9999;background:#fff}.content_right_menu li,.file_right_menu li,.selection_right_menu li{list-style-type:none;margin:0;padding:0;height:30px;font-size:12px;color:#555;line-height:30px;padding-left:48px;box-sizing:border-box;position:relative;font-weight:400;border-radius:2px}.selection_right_menu li span{color:#666}.content_right_menu li.separate,.file_right_menu li.separate,.selection_right_menu li.separate{height:1px;background:#ececec}.content_right_menu li:hover,.file_right_menu li:hover,.selection_right_menu li:hover{background:#efefef;cursor:pointer}.selection_right_menu li:hover .file_menu_down .set_group{display:inline-block}.content_right_menu li .file_menu_icon,.file_right_menu li .file_menu_icon,.nav_down_list li .file_menu_icon,.nav_group .nav_btn_group .file_menu_icon,.selection_right_menu li .file_menu_icon{display:inline-block;background-image:url(../img/file_icon/file_menu_icon.png);background-size:auto!important;background-repeat:no-repeat;width:16px;height:16px;position:absolute;left:22px;top:50%;margin-top:-8px;font-style:normal}.selection_right_menu li .property_file_icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA7klEQVQ4T6WTQUrDUBCG///lBnZboeAFvEBvUcWV0BIy3dadJ3CnSzNJcF20d+giF/AALRTi1t7gZaQhkVJ0kb5ZDMyb+T+YNzPEiRVFceG9nwAYkxwf0mZWAiijKFrFcbw/lvA4yLLszsyeAIxOwW28I/mYJMmyy/8CVNX+Ef35LCKNtnGq+gxg0QcA4EVEHpjn+bCu608Ag56Ab+fcNdM0nZJ86yluys1sdgC8kpyfCUipqhsAVy3gQ0RuVbUCMCR5772vnHNrAF8icqmq7wBu2vptOCC4heBPDB5j8CJ14wta5Q4SdEwdpO85/wAdKopJjTYPqgAAAABJRU5ErkJggg==)!important}.nav_down_list:before{content:'';display:inline-block;width:100%;height:5px;position:absolute;top:-5px}.edit_file_icon,.open_file_icon,.open_find_dir_file_icon{background-position:-16px -16px}.copy_file_icon{background-position:0 -96px}.paste_file_icon{background-position:-16px -64px}.shear_file_icon{background-position:-16px -80px}.rename_file_icon{background-position:0 -64px}.del_file_icon{background-position:0 -80px}.download_file_icon{background-position:-16px -48px}.folad_file_icon,.killing_file_icon,.unzip_file_icon{background-position:0 -32px}.decompression_file_icon{background-position:0 -32px}.authority_file_icon,.cancel_share_file_icon,.dir_kill_file_icon,.share_file_icon{background-position:-17px -773px;background-image:none!important;width:17px!important;height:17px!important;font-size:19px;top:0!important;margin-top:0!important;color:#31dc48}.dir_kill_file_icon{font-size:17px;color:#c77f22}.authority_file_icon{color:#f2ce4d}.cancel_favorites_file_icon,.favorites_file_icon{background-position:0 -32px;background-position:-17px -773px;width:17px!important;height:17px!important;margin-top:-8.5px!important}.file_menu_down .glyphicon-triangle-right{font-size:8px;position:absolute;right:8px;top:8px;color:#555;transform:scale(.8)}.selection_right_menu.left_menu .file_menu_down .set_group{top:-6px;left:-210px}.file_menu_down .set_group{display:none;padding:5px 0;background:#fff;width:210px;border:1px solid #dadada;position:absolute;right:-210px;top:-6px;box-shadow:5px 5px 12px rgba(0,0,0,.15)}.file_right_menu li:hover .set_group{display:inline-block}.file_right_menu li .power_file_icon,.nav_group .nav_btn_group .power_file_icon{background-image:url(../img/file_icon/power_file_icon.png)!important;background-size:16px!important}.add_favorites_icon{background-position:-16px -773px}.refresh_file_icon{background-position:-16px -513px}.upload_file_icon{background-position:0 -48px}.create_dir_file_icon,.create_file_icon,.newly_file_icon{background-position:0 -16px}.compress_file_icon,.rar_file_icon,.tar_gz_file_icon,.zip_file_icon{background-position:-16px -32px}.paste_file_icon{background-position:-16px -64px}.total_icon_icon{background-position:0 -561px}.total_list_icon{background-position:-16px -625px}.selection_right_menu li .file_menu_icon.web_shell_file_icon{background-image:url(../img/file_icon/ico-cmd.png)}.onselectstart{position:fixed;display:none;width:0;height:0;background:#bbf1e3;border:1px dashed #a1d0c3;opacity:.5}.file_list_header .file_check.active,.file_list_header .file_check.active_2,.file_tr.active .file_check{background:#5fb878!important;border:1px solid #5fb878!important;color:#fff}.file_list_header .file_check.active_2::before{font-weight:700;content:"";height:2px;width:10px;display:inline-block;border-radius:1px;position:relative;background-color:#fff;top:-4px;left:2px}.file_list_header .file_check.active::before,.file_tr.active .file_check::before{font-weight:700;content:"\e75e";position:relative;top:-2px;font-size:14px;font-family:iconfont!important;font-style:normal;-webkit-font-smoothing:antialiased}.statistics{display:inline-block;height:30px;line-height:30px;padding:0 10px;position:absolute;border-left:4px solid #20a53a;bottom:38px;font-size:14px}.statistics span{margin:0 2px;font-size:14px}.bt_file_updated{position:fixed;width:100%;height:100%;left:0;top:0;right:0;bottom:0;background:rgba(32,165,58,.5);z-index:9999;display:none}.updated_title{text-align:center;position:absolute;z-index:99999;color:#fff;width:100%;font-size:50px;opacity:.8;top:50%}.updated_icon{position:absolute;top:50%;left:50%;margin-left:-125px;margin-top:-200px}.updated_icon .btn{width:250px;height:200px;position:relative;text-align:center;margin:15px;overflow:hidden;border-radius:100%;display:inline-block}.updated_icon .cloud{width:200px;height:80px;position:relative;background:#fff;display:inline-block;border-radius:50px;margin:40px auto 0}.updated_icon .cloud:after{content:'';position:absolute;width:120px;height:80px;border-radius:80px 80px 0 0;top:-40px;background:#fff;left:40px}.updated_icon .cloud:before{content:'';position:absolute;background:#dedede;border-radius:10px;width:90px;height:6px;z-index:2;left:0;right:0;bottom:25px;margin:auto}.updated_icon .file{position:absolute;left:0;right:0;margin:auto;z-index:5;top:50px;width:80px;height:100px;display:inline-block;overflow:hidden}.updated_icon .file:after{content:'';position:absolute;background:#5fb878;width:80px;height:80px;left:0;top:0;border-radius:0 0 10px 10px;-webkit-animation:uplaod 3s linear infinite;animation:uplaod 3s linear infinite}.updated_icon .file:before{content:'';background:#e2e3e3;color:#e2e3e3;width:60px;height:5px;position:absolute;left:0;right:0;top:20px;z-index:3;margin:auto;-webkit-animation:uplaod 3s linear infinite;animation:uplaod 3s linear infinite;box-shadow:0 15px,0 30px}@-webkit-keyframes uplaod{0%{-webkit-transform:translateY(0);transform:translateY(0)}100%{-webkit-transform:translateY(-100px);transform:translateY(-100px)}}@keyframes uplaod{0%{-ms-transform:translateY(0);-webkit-transform:translateY(0);transform:translateY(0)}100%{-ms-transform:translateY(-100px);-webkit-transform:translateY(-100px);transform:translateY(-100px)}}.file_nav_view{min-height:32px;margin:15px 0;font-size:0}.file_nav_view .nav_group{display:inline-block;vertical-align:top}.file_nav_view .nav_group.dividing{border-color:#fff;height:100%;width:1px;background:#cfcfcf;cursor:default;margin:0 10px}.nav_group .nav_btn:hover,.nav_group .nav_btn_group:hover{background-color:#20a53a;border-color:#20a53a}.nav_group .nav_btn:hover .nav_btn_title,.nav_group .nav_btn_group:hover .nav_btn_title{color:#fff}.nav_group .nav_btn:hover>.iconfont,.nav_group .nav_btn_group:hover>.iconfont{color:#fff}.file_nav_view .nav_btn_group.active:hover{border-color:#cfcfcf}.file_nav_view .nav_btn:hover .nav_down_list,.file_nav_view .nav_btn_group:hover .nav_down_list{display:block}.file_nav_view .nav_btn::before{content:'';display:none;width:100%;height:5px;position:absolute;bottom:-5px;left:0}.file_nav_view .nav_btn:hover::before{display:inline-block}.nav_group .nav_btn,.nav_group .nav_btn_group{padding:0 10px;display:inline-block;box-sizing:border-box}.terminal_view .nav_btn_title{vertical-align:middle!important;margin-left:5px}.terminal_view{padding:4px 10px!important}.terminal_view .iconfont{vertical-align:middle}.nav_group .nav_btn,.nav_group .nav_btn_group{display:inline-block;margin-right:5px;border:1px solid #cfcfcf;box-sizing:border-box;cursor:pointer;border-radius:2px;transition:all .1s;position:relative;padding:7px 10px}.nav_group{font-size:0}.nav_group .share_file_list{margin-right:8px}.nav_group .nav_btn_group{margin-right:0;vertical-align:bottom}.nav_group .nav_btn_group{padding-left:28px}.nav_group .nav_btn_group .file_menu_icon{left:7.5px}.nav_group .nav_btn .icon-xiala,.nav_group .nav_btn_group .icon-xiala{font-size:12px;margin-left:2px;vertical-align:middle}.nav_group .nav_btn .nav_btn_title,.nav_group .nav_btn_group .nav_btn_title{font-size:12px;color:#555;vertical-align:top}.nav_group .nav_btn_group{border-right-width:1px;border-left-width:0;border-top-right-radius:0;border-bottom-right-radius:0;border-top-left-radius:0;border-bottom-left-radius:0}.nav_group .nav_btn_group.active{background:#ececec}.nav_group .nav_btn_group:last-child{border-right-width:1px;border-top-right-radius:2px;border-bottom-right-radius:2px}.nav_group .nav_btn_group:first-child{border-left-width:1px;border-top-left-radius:2px;border-bottom-left-radius:2px}.nav_group .nav_btn.nav_btn_title .icon-terminal,.nav_group .nav_btn_group .nav_btn_title .icon-terminal{font-size:17px}.nav_group .nav_btn .icon-shangchuanxiazai,.nav_group .nav_btn_group .icon-shangchuanxiazai{font-size:12px;margin-right:5px;color:#555}.nav_group.mount_disk_list{padding-left:8px;border-left:1px solid #ececec}.nav_group.multi{padding-right:8px;border-right:1px solid #ececec}.nav_group.mount_disk_list>.glyphicon{font-size:13px;color:#666;position:relative;top:2px}.float_r.menu-header-foot{font-size:0}.nav_group.file_all_paste,.nav_group.recycle_bin{padding-left:8px}.nav_group.recycle_bin .nav_btn{margin-right:8px}.nav_group.mount_disk_list .nav_btn,.nav_group.recycle_bin .nav_btn{padding:7px 10px 7px 5px}.nav_group.mount_disk_list .nav_btn span,.nav_group.recycle_bin .nav_btn span{font-size:12px;margin-left:5px;color:#666}.file_disk_list .nav_btn span:nth-child(0){font-size:12px}.nav_group.recycle_bin .nav_btn .glyphicon{top:2px}.nav_group.mount_disk_list .nav_btn:hover span,.nav_group.recycle_bin .nav_btn:hover span{color:#fff!important}.nav_down_list{display:none;transition:all .5s;list-style:none;background:#fff;border-radius:2px;box-shadow:4px 2px 5px 0 #ececec;border:1px solid #cfcfcf;position:absolute;left:-1px;top:32px;z-index:999999;overflow:auto;max-height:300px;max-width:140px}.nav_group.mount_disk_list .disk_title_group_btn{display:none}.nav_group.mount_disk_list.thezoom .disk_title_group_btn{border-radius:2px;height:32px;line-height:32px;border:1px solid #cfcfcf;padding:7px 10px;display:inline-block;width:115px;box-sizing:border-box;cursor:pointer;position:relative;transition:all .5s}.nav_group.mount_disk_list .file_disk_list .nav_btn:last-child{margin-right:0!important}.nav_group.mount_disk_list.thezoom .file_disk_list{display:none}.nav_group.mount_disk_list.thezoom .disk_title_group_btn .disk_title_group{font-size:12px;color:#666;position:absolute;top:0}.nav_group.mount_disk_list.thezoom .disk_title_group_btn::before,.nav_group.multi.thezoom .batch_multi_title::before{content:'';display:none;width:100%;height:5px;position:absolute;bottom:-5px;left:0}.nav_group.mount_disk_list.thezoom .disk_title_group_btn:hover::before,.nav_group.multi.thezoom .batch_multi_title:hover::before{display:inline-block}.nav_group.mount_disk_list.thezoom .disk_title_group_btn:hover,.nav_group.multi.thezoom .batch_multi_title:hover{background-color:#20a53a;border-color:#20a53a}.nav_group.mount_disk_list.thezoom .disk_title_group_btn:hover .disk_title_group,.nav_group.mount_disk_list.thezoom .disk_title_group_btn:hover>i,.nav_group.multi.thezoom .batch_multi_title:hover>i,.nav_group.multi.thezoom .batch_multi_title:hover>span{color:#fff}.nav_group.mount_disk_list.thezoom .disk_title_group_btn>i{font-size:12px;margin-left:2px;position:absolute;right:9px;top:0}.nav_group.mount_disk_list.thezoom .disk_title_group_btn .nav_down_list{max-width:360px}.nav_group.mount_disk_list.thezoom .disk_title_group_btn .nav_down_list li i{font-size:13px;position:absolute;left:8px;top:8px;color:#666}.nav_group.mount_disk_list.thezoom .disk_title_group_btn .nav_down_list li span{width:100%;padding-right:10px}.nav_group.mount_disk_list.thezoom .disk_title_group_btn:hover ul.nav_down_list{display:block}.nav_group.multi.thezoom{padding-right:0;border-right:none}.nav_group.multi .batch_multi_title>i,.nav_group.multi .batch_multi_title>span{display:none}.nav_group.multi.thezoom .batch_multi_title>i,.nav_group.multi.thezoom .batch_multi_title>span{display:inline-block;font-size:12px;color:#666}.nav_group.multi.thezoom .batch_multi_title>i{font-size:12px;margin-left:2px;position:absolute;right:9px;top:7px}.nav_group.multi.thezoom{position:relative}.nav_group.multi.thezoom:hover .batch_group_list{display:block}.nav_group.multi.thezoom .batch_multi_title{border-radius:2px;height:32px;border:1px solid #cfcfcf;padding:7px 10px;display:inline-block;width:85px;box-sizing:border-box;cursor:pointer;position:relative}.nav_group.multi.thezoom .batch_group_list{display:none;width:85px;margin-top:12px;position:absolute;left:0;border:1px solid #cfcfcf;border-radius:2px;box-shadow:4px 2px 5px 0 #ececec;z-index:9999;background-color:#fff}.nav_group.multi.thezoom .batch_group_list .nav_btn_group{width:100%;border:none}.nav_group.multi.thezoom .batch_group_list .nav_btn_group:hover{background-color:#f0f0f0}.nav_group.multi.thezoom .batch_group_list .nav_btn_group:hover span{color:#666}.selection_right_menu li .file_menu_icon.create_files_file_icon{height:17px;background-image:url(../img/file_icon/file_text2.png)}.file_nav_view .nav_down_list li i.file_new_icon{width:17px;height:17px;position:absolute;top:7.5px;left:5px;background-repeat:no-repeat;background-size:cover;background-position:center;image-rendering:-webkit-optimize-contrast;background-image:url(../img/file_icon/file_text2.png)}.file_nav_view .nav_down_list li i.file_new_folder_icon{width:19px;height:17px;position:absolute;top:7.5px;left:4px;background-repeat:no-repeat;background-size:cover;background-position:center;image-rendering:-webkit-optimize-contrast;background-image:url(../img/file_icon/folder.png)}.file_list_content .file_tr textarea:focus{outline:0!important;border:1px solid #ccc}.file_table_view .file_list_content .file_tr.editr_tr textarea,.file_table_view.icon_view .createModel textarea{width:70px;padding:5px 2px;border-color:#ccc;overflow:hidden;resize:none}.file_table_view .file_list_content .file_tr.editr_tr input,.file_table_view.list_view .createModel input{width:100%;padding:0 0 0 5px;height:25px;border:1px solid #ececec;outline:0;vertical-align:middle;line-height:25px}.favorites_file_path .nav_down_list{max-width:230px}.favorites_file_path .nav_down_list li span{width:230px!important;padding-right:20px}.favorites_body .favorites_span{width:680px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.favorites_file_path .nav_down_list li span.icon-shezhi1{left:6px}.nav_down_list li{height:30px;line-height:30px;position:relative;box-sizing:border-box;overflow:hidden;color:#555}.nav_down_list li:last-child{border:none}.nav_down_list li:hover{background:#f0f0f0;color:#555}.file_dir_item .nav_down_list li span{line-height:30px!important}.file_dir_item .file_dir span{color:#666}.nav_down_list li span:nth-child(1){font-size:14px;position:absolute;line-height:28px;top:0;left:12px;color:#666}.nav_down_list li span:nth-child(2){display:inline-block;font-size:12px;color:#555;line-height:30px;padding-left:28px;vertical-align:top;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:140px}.nav_down_list li .iconfont{color:#aaa;font-size:19px;position:absolute;left:5px}.nav_down_list li .file_menu_icon{left:5px;margin-top:-8.5px}.nav_down_list li .icon-yingpan{font-size:15px}.nav_group.btn-group-sm>.btn.cut_view_model{padding:6px 10px}.file_path_views{height:30px;border-bottom:1px solid #eee;font-size:0;box-sizing:content-box;padding-bottom:15px}.file_path_views .file_path_upper{display:inline-block;height:30px;line-height:30px;vertical-align:top;border:1px solid #cfcfcf;box-sizing:border-box;text-align:center;vertical-align:top;border-top-left-radius:2px;border-bottom-left-radius:2px;border-right:none;color:#666}.file_path_views .file_path_upper{padding:0 12px}.file_path_views .file_path_upper:hover{background-color:#20a53a;color:#fff;cursor:pointer;border:1px solid #20a53a;border-right:none}.file_path_views .file_path_upper:hover i{background-position:-60px 6px}.file_path_views .file_path_upper span{font-size:12px;display:inline-block;height:28px;line-height:28px;vertical-align:top;top:0}.file_path_views .forward_path{display:inline-block;height:30px;line-height:30px;vertical-align:top;margin-right:-1px}.file_path_views .forward_path>span{display:inline-block;text-align:center;height:30px;line-height:30px;width:auto;padding:0 9px;border:1px solid #cfcfcf;box-sizing:border-box;border-radius:2px;cursor:pointer}.file_path_views .forward_path>span span{font-size:15px}.file_path_views .forward_path span:hover{background:#20a53a;border:1px solid #20a53a}.file_path_views .forward_path span:hover:first-child i{background-position:-90px 6px}.file_path_views .forward_path span:hover:last-child i{background-position:-75px 6px}.file_path_views .forward_path span.active:hover{background:#efefef;box-shadow:inset 0 3px 6px rgba(0,0,0,.1);border-color:#cfcfcf}.file_path_views .forward_path span:active{box-shadow:inset 0 3px 6px rgba(0,0,0,.1)}.file_path_views .forward_path span.active{background:#efefef;box-shadow:inset 0 3px 6px rgba(0,0,0,.1);cursor:no-drop;transition:all .5s}.file_path_views .forward_path span:first-child{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.file_path_views .forward_path span:last-child{border-left:1px solid #cfcfcf;border-radius:0}.file_path_input{display:inline-block;height:30px;width:50%;box-sizing:border-box;position:relative}.file_path_input input{border:1px solid #cfcfcf;display:inline-block;width:100%;position:absolute;top:0;left:0;height:30px;padding-left:5px;box-sizing:border-box;background:#f3f3f3;font-size:13px}.file_path_input input.focus{background-color:#fff}.file_dir_omit{display:inline-block;font-size:0;height:30px;line-height:28px;position:relative;left:0;width:45px;cursor:pointer;border:1px solid transparent}.file_dir_omit.active{background-color:#f0f9f7;border-top:1px solid #cfcfcf;border-bottom:1px solid #cfcfcf;border-right:1px solid #ececec}.file_dir_omit.active .iconfont{background-color:#f0f9f7;border-left:#dadada;border-right:#dadada}.file_dir_icon,.file_dir_omit>span{display:inline-block;font-size:14px;background-image:url(../img/file_icon/file_menu_icon.png);background-position:0 -16px;background-size:auto;height:16px;position:absolute;top:6px;width:16px;left:5px;z-index:99}.file_dir_icon{left:8px;top:7px}.file_dir_omit .iconfont{position:absolute;top:0;right:0;font-size:12px!important;padding:0 2px}.file_dir_omit:hover{background:#f3fbf9;border-top:1px solid #cfcfcf;border-bottom:1px solid #cfcfcf;border-right:1px solid #ececec}.file_dir_omit:hover .iconfont{background:#f3fbf9;border-left:1px solid #ececec}.file_dir_omit .nav_down_list{overflow:hidden}.file_dir_omit .nav_down_list .file_dir_item{display:block;height:32px;position:relative;box-sizing:border-box}.file_dir_omit .nav_down_list .file_dir_item .file_dir{width:140px;background:#fff;height:32px;line-height:32px;padding-left:28px}.file_dir_omit .nav_down_list .file_dir_item .iconfont,.file_dir_omit .nav_down_list .file_dir_item .nav_down_list{display:none}.file_dir_item{display:inline-block;font-size:0;height:28px;line-height:28px;cursor:pointer;color:#777;box-sizing:border-box;position:relative;top:1px!important}.file_dir_item.active .nav_down_list{display:block;top:30px}.file_dir_view{position:relative;font-size:0;z-index:99;display:inline-block}.file_dir_view .file_dir_item.active{background:#f3fbf9;border:1px solid #d1e8e0;box-sizing:border-box;margin-top:-1px;box-shadow:inset 0 0 2px rgba(0,0,0,.1)}.file_dir_view .file_dir_item.active .iconfont{border-left:1px solid #d1e8e0}.file_dir_item .iconfont,.file_dir_omit .iconfont{vertical-align:top;font-size:14px;font-weight:600;height:28px;display:inline-block;border-left:1px solid #f3f3f3;border-right:1px solid #f3f3f3;transition:all .5s;background:#f3f3f3}.file_dir_item .file_dir{display:inline-block;padding:0 5px;font-size:12.5px;position:relative;height:28px;line-height:29px;color:#555;background:#f3f3f3;border-left:1px solid transparent;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file_dir_item .nav_down_list li{height:30px;line-height:30px;background:#f9f9f9}.file_dir_item .nav_down_list li.active{font-weight:600;color:#333;background-color:#eee}.file_dir_item .nav_down_list li:hover{background:#eee}.file_dir_item:first-child .file_dir{position:relative;top:0;font-size:12.4px;padding-left:10px}.file_dir_item:first-child{left:-1px}.file_dir_item:first-child .file_dir span:first-child{vertical-align:text-top}.file_dir_view .file_dir_item:hover{background:#f3fbf9;box-shadow:inset 0 0 2px rgba(0,0,0,.1)}.file_dir_view .file_dir_item:hover .file_dir{background:#f3fbf9;border-left:1px solid #ececec}.file_dir_view .file_dir_item:hover .iconfont{border-left:1px solid #ececec;border-right:1px solid #ececec;background:#f3fbf9}.file_path_refresh{display:inline-block;width:30px;height:30px;line-height:30px;border:1px solid #cfcfcf;box-sizing:border-box;text-align:center;vertical-align:top;border-left:0;border-top-right-radius:2px;border-bottom-right-radius:2px;cursor:pointer}.file_path_refresh:hover i{background-position:-105px 6px}.file_path_collect:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.file_path_collect i{font-size:14px;position:relative;top:-1px}.file_path_collect .icon-wujiaoxing{color:#ffca2e}.file_path_collect:hover{background:#20a53a;border-color:#38983b;color:#fff}.search_path_views{border:1px solid #20a53a;display:inline-block;vertical-align:top;float:right;font-size:0;position:relative}.search_path_views .set_updown{height:30px;line-height:30px;position:absolute}.file_path_views .forward_path i{top:0}.search_path_views .file_search_input{border:none;display:inline-block;height:28px;box-sizing:border-box;width:250px;padding-left:8px;box-sizing:border-box;outline:0;border-right:none;font-size:12px}.search_path_views .is_search_children{margin:0 5px;color:#cfcfcf;font-size:13px}.search_path_views .is_search_children:hover{color:#20a53a;cursor:pointer}.search_path_views .path_btn{display:inline-block;height:28px;background:#20a53a;vertical-align:top;color:#fff;border:none;width:35px;text-align:center;cursor:pointer;outline:0}.search_path_views .path_btn:hover{background-color:#38983b}.search_path_views .path_btn:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.search_path_views .set_up{display:none;position:absolute;top:30px;left:0;width:210px;height:30px;background:#fff;box-shadow:4px 2px 5px 0 #ececec;z-index:9999;border:1px solid #ececec;border-radius:2px}.search_path_views .file_search_config{position:absolute;right:40px;font-size:12px;color:#666;width:105px;border-top:none;background:#fff;height:28px;line-height:28px;top:0}.search_path_views .file_search_config .file_search_checked{position:relative;height:15px;width:15px;border-radius:1px;cursor:pointer;text-align:center;margin:0;vertical-align:sub;border:1px solid #aaa;border-radius:2px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased}.search_path_views .file_search_config .file_search_checked.active{background:#20a53a;color:#fff;border-color:#20a53a}.search_path_views .file_search_config .file_search_checked.active:after{content:"\e013";font-size:12px;transform:scale(.85);position:absolute;left:.75px;top:.75px}.search_path_views .file_search_config label{position:absolute;right:3px;cursor:pointer;top:0;font-weight:400}.mr0{margin-right:0!important}.ml0{margin-left:0!important}.plr25{padding:0 25px}.float_r{float:right}.file_bodys{background-color:#fff;margin:10px 15px;padding:15px 20px;font-size:12px;-webkit-user-select:none;-moz-user-select:none;-o-user-select:none;user-select:none}.file_list_icon{border-top:1px solid #eee;padding-top:15px}.file_list_icon .file_table_view.list_view .file_list_content .file_tr{display:inline-block;width:auto;background:#fff;border-color:#fff;position:relative;height:auto;margin:2.5px 5px;cursor:pointer;vertical-align:top}.file_table_view.list_view .file_list_content .file_tr.selected{background:#f0f9f7!important}.file_list_icon .file_table_view.list_view .file_list_content .file_tr .file_td{height:auto;padding:0;line-height:normal}.file_list_icon .file_table_view.list_view .file_list_content .file_tr .file_td:not(.file_name){display:none}.file_list_icon .file_table_view.list_view .file_list_content .file_tr .file_td:nth-of-type(1){position:absolute;left:0;top:0}.file_list_icon .file_table_view.list_view .file_list_content .file_tr .file_td:nth-of-type(2){width:99px;min-width:99px}.file_list_icon .file_table_view.list_view .file_list_content .file_type{width:100%;height:65px}.file_list_icon .file_table_view.list_view .file_list_content .file_icon{top:15px;width:50px;height:50px;left:50%;margin-left:-25px;background-size:auto}.file_list_icon .file_title{display:inline-block;text-align:center;color:#666;padding:10px 5px 15px 5px;font-size:12px;word-break:break-all;white-space:initial}[data-type=folder] .file_title{cursor:pointer}.file_tips{margin-left:30px;color:#999;font-size:12px}.file_bodys{margin-bottom:57px;-webkit-box-sizing:initial;-moz-box-sizing:initial;box-sizing:initial}.file_path_input{position:relative}.file_path_input .file_dir_view{white-space:nowrap;position:absolute;left:1px;height:30px;line-height:28px}.not-btn-border .layui-layer-btn{background-color:#fff;border-top:none}.not-btn-border .layui-layer-btn a{line-height:30px;padding:0 15px}.not-btn-border .layui-layer-btn0{margin-left:15px;border-radius:2px;padding:0 15px}.not-btn-border .layui-layer-btn1{border-radius:2px}.not-btn-border .layui-layer-btn{padding:9px 15px 10px}.file_upload_view{padding:10px 15px 0 15px}.file_select_file{padding-bottom:10px}.file_select_file button{padding:7.5px 12px;border-radius:2px;border:none;background-color:#4592f0;border-color:#367fa9;margin-right:15px;color:#fff;outline:0}.file_select_file button:hover{background-color:#4188de;border-color:#367fa9}.file_select_file button:active{box-shadow:inset 0 3px 6px rgba(0,0,0,.2)}.file_content_list{width:100%;height:355px;background-color:#fafbfe;border-radius:2px;border:#e4e5e9 1px solid;position:relative}.file_upload_tips{text-align:center;color:#ccc;font-size:28px;position:absolute;width:100%;height:40px;line-height:40px;top:50%;margin-top:-40px}.upload_or_download .glyphicon{color:#666}.upload_or_download_view{padding:10px 20px}.upload_btn_group{margin-top:15px;display:inline-block;text-align:right;width:100%}.upload_btn_group button{margin-right:15px}.upload_btn_group button:last-child{margin:auto}.filePage{width:100%;height:auto}.filePage>div{margin-top:10px}.filePage>.page_num{float:left;line-height:30px;padding:0 5px}.filePage>.page_num a{float:initial;border:none;line-height:30px;color:#20a53a;display:inline;font-size:13px;padding:0 5px}.filePage>.page_num a:hover{background:#fff;color:#20a53a;text-shadow:0 1px 0 #fff;cursor:pointer}.filePage.showRow{margin:0 3px;border:#cfcfcf 1px solid}.filePage .page span{height:30px;line-height:30px;padding:0 12px}.filePage .Pcount-item{border-left:none;border-right:1px solid #ececec;color:#333}.filePage .Pcount-item .showRow{margin:0 3px;border:1px solid #eee;border-radius:2px}.preview_images_mask{position:fixed;left:50%;top:50%;background:rgb(0 0 0 /.85);z-index:9999;height:650px;width:750px;margin-left:-375px;margin-top:-325px;overflow:hidden;-webkit-box-shadow:0 0 3px 1px rgba(0,0,0,.3);-moz-box-shadow:0 0 3px 1px rgba(0,0,0,.3);box-shadow:0 0 3px 1px rgba(0,0,0,.3);border-radius:4px}.preview_images_mask .preview_head{height:45px;line-height:45px;width:100%;position:absolute;z-index:999}.preview_images_mask .preview_title{text-align:center;width:100%;display:inline-block;font-size:16px;background-color:rgb(0 0 0 /.45);color:#cacaca;cursor:all-scroll}.preview_images_mask .preview_close,.preview_images_mask .preview_full,.preview_images_mask .preview_small{position:absolute;right:0;width:45px;height:45px;line-height:47px;font-size:19px;text-align:center;color:#d6d6d6}.preview_images_mask .preview_full,.preview_images_mask .preview_small{right:45px;font-size:18px}.preview_images_mask .preview_close:hover,.preview_images_mask .preview_full:hover,.preview_images_mask .preview_small:hover{background-color:#444;color:#fff;cursor:pointer}.preview_images_mask .preview_body{position:relative;height:100%;z-index:99;padding:45px 0}.preview_images_mask:hover .preview_cut_view{opacity:1}.preview_images_mask:hover .preview_toolbar{opacity:1}.preview_images_mask .preview_body img{position:absolute;display:none;transition:all .2s;cursor:all-scroll;display:inline-block;opacity:0;webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.preview_images_mask .preview_toolbar{position:absolute;bottom:0;width:250px;left:50%;margin-left:-125px;height:45px;border-radius:3px;background-color:rgb(0 0 0 /.65);font-size:18px;z-index:999;opacity:0;transition:all 1s}.preview_images_mask .preview_toolbar a{width:50px;height:45px;line-height:45px;text-align:center;display:inline-block;transition:all .5s;border-radius:3px}.preview_images_mask .preview_toolbar a:hover{background-color:rgb(86 86 86 / 75%)}.preview_images_mask .preview_toolbar span{vertical-align:middle;color:#bbb}.preview_images_mask .preview_toolbar span.reverse-repeat{transform:rotateY(180deg)}.preview_images_mask .preview_cut_view{opacity:0;transition:all .5s}.preview_images_mask .preview_cut_view a:hover{background-color:#3a3a3a;color:#fff}.preview_images_mask .preview_cut_view a{position:absolute;font-size:26px;width:50px;height:50px;text-align:center;line-height:50px;top:50%;margin-top:-25px;background-color:rgb(194 194 194 / 80%);border-radius:50%;transition:all .2s;z-index:999}.preview_images_mask .preview_cut_view a:nth-child(1){left:3%}.preview_images_mask .preview_cut_view a:nth-child(2){right:3%}.preview_images_mask .preview_cut_view span{vertical-align:top;height:50px;width:50px;text-align:center;line-height:50px}.exists_files_style{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block;width:145px}.readonly-form-input:focus{outline:0;background-color:-internal-light-dark(rgba(239,239,239,.4),rgba(59,59,59,.4));cursor:default}.readonly-form-input:active{border-color:#ccc;box-shadow:none;-webkit-box-shadow:none}.icon-file-upper{background-position:0 6px}.icon-file-back{background-position:-30px 6px}.icon-file-forward{background-position:-15px 6px}.icon-file-refresh{background-position:-45px 6px}.icon-file{display:inline-block;background-image:url(../images/iconfont-file.png);height:28px;line-height:28px;width:15px;background-repeat:no-repeat}.bt-warp{min-width:1400px}.file_menu_tips{display:inline-block;font-size:14px;line-height:32px}.file_menu_tips span{vertical-align:middle;margin-right:3px;color:#f0ad4e}.file_menu_tips span:nth-child(2){font-size:12px}.file_path_collect:hover,.file_path_refresh:hover{background:#20a53a;border-color:#38983b;color:#fff}.bt-property-setting .tab-nav{font-size:0}.bt-property-setting .tab-nav span{font-size:12px}.bt-property-setting .tab-con{padding:5px 0 0}.bt-property-setting .property-box{display:none}.bt-property-setting .property-box.active{display:inline-block;width:100%}.bt-property-setting .property-box .attr-box{margin:5px 0;width:100%}.bt-property-setting .property-box .attr-name{width:120px;padding-left:10px;float:left}.bt-property-setting .property-box .attr-content{margin-left:120px}.bt-property-setting .property-box .attr-content input{height:32px;line-height:32px;border:1px solid#ccc;outline:0;padding-left:5px;width:350px;border-radius:2px}.bt-property-setting .property-box .attr-content input:focus{border-color:#20a53a}.bt-property-setting .property-box .file_icon{height:50px;width:50px;position:relative;display:inline-block;left:-10px;top:2px}.bt-property-setting .property-box .dividing{width:100%;height:1px;margin:10px 0;background-color:#ececec}.bt-property-setting .property-box .attr-box .attr-content,.bt-property-setting .property-box .attr-box .attr-name{height:25px;line-height:25px}.bt-property-setting .property-box .attr-box .attr-content span{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.details_box_view,.history_box_view{border:1px solid #ececec}.details_box_view,.history_box_view,.property-box{height:386px !important;overflow:auto}.details_box_view table,.history_box_view table{width:100%}.details_box_view thead tr,.history_box_view thead tr{background-color:#f6f6f6;color:#555;font-weight:400}.details_box_view tbody tr:hover,.history_box_view tbody tr:hover{background:#f0f9f7;cursor:pointer}.details_box_view td div,.details_box_view th div,.history_box_view td div,.history_box_view th div{padding:5px;height:30px;line-height:20px}.details_box_view thead,.history_box_view thead{border-bottom:1px solid #f6f6f6}.ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block} \ No newline at end of file diff --git a/BTPanel/static/css/login.css b/BTPanel/static/css/login.css index 7f83ce2a..29b45304 100644 --- a/BTPanel/static/css/login.css +++ b/BTPanel/static/css/login.css @@ -1,725 +1 @@ -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: 155px -} - -.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: 200px; - 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 +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{padding:5px 0;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:0 0;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,.4) inset,-1px 0 3px -2px rgba(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:700}.webEdit-box input,.webEdit-box select,.webEdit-box textarea,.zun-form-new input[type=email],.zun-form-new input[type=number],.zun-form-new input[type=password],.zun-form-new input[type=text],.zun-form-new select,.zun-form-new textarea{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}.dataTables_paginate select:active,.dataTables_paginate select:focus,.dlt-text:focus,.sp-text:focus,.webEdit-box input:active,.webEdit-box input:focus,.webEdit-box select:active,.webEdit-box select:focus,.zun-form-new input:active,.zun-form-new input:focus,.zun-form-new select:active,.zun-form-new select:focus,textarea:active,textarea: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}.zun-form-new textarea{height:100px;padding:5px 0 0 5px;width:80%}.getfile-btn,.zun-form-new .submit-btn{text-align:right;padding:7px 20px 10px;background:#f6f8f8;border-top:1px solid #edf1f2;margin-top:10px;clear:both}.getfile-btn button:first-child,.zun-form-new .submit-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,.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%}}.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,.4) inset,-1px 0 3px -2px rgba(0,0,0,.1) inset;border-radius:3px;outline:0;box-sizing:border-box}.verification_view .v_input{margin-bottom:15px}.verification_view .v_tips{color:#777;font-size:13px;margin-top:18px}body{line-height:1.4;color:#333;font-family:"微软雅黑",Arial,Helvetica,sans-serif;font-size:12px}::-webkit-scrollbar{width:10px;height:1px}::-webkit-scrollbar-thumb{border-radius:10px;box-shadow:inset 0 0 5px rgba(0,0,0,.2);background:#999}::-webkit-scrollbar-track{box-shadow:inset 0 0 5px rgba(0,0,0,.2);border-radius:10px;background:#ededed}@media screen and (max-width:640px){.scanCode{display:none}.account{display:block!important}.bg_img{display:none}}.bg_img{width:60px;height:60px;background-image:url(/static/img/qrCode.png);background-size:120px 120px;background-repeat:no-repeat;background-position:0 0;position:absolute;top:5px;right:5px}.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{text-align:center;padding-top:20px}.scanCode .titles{font-size:23px;margin-top:25px;color:#444;text-align:center}.list_scan{width:160px;margin:0 auto;margin-top:15px;text-align:center}.list_scan>img{width:40px;height:40px;float:left;margin-right:10px}.list_scan span{display:inline-block;font-size:13px;margin-bottom:5px}.list_scan a:hover .weChatSamll{display:block}.list_scan .weChatSamll{display:none;width:150px;height:150px;position:absolute;border:1px solid #ececec;border-radius:5px;bottom:105px;right:80px;padding:20px;background-color:#fff}.list_scan .weChatSamll img{width:100%}.list_scan .weChatSamll em{position:absolute;border:7px solid #ececec;border-color:#ececec transparent transparent transparent;width:0;height:0;right:87px;bottom:-14px;margin-left:-6px}.tips{position:absolute;top:20px;right:60px;color:#20a53a;background:#dff0d8;padding:5px 10px;text-align:center;border-radius:4px}.tips em{position:absolute;border:6px solid #dff0d8;border-color:transparent transparent transparent #dff0d8;width:0;height:0;right:-11px;top:8px;margin-left:-6px}.tips span>span{vertical-align:middle}.tips img{height:16px;width:16px;vertical-align:middle;margin-top:-1px;margin-right:4px}.main .login .rlogo{margin-top:20px;margin-bottom:25px;padding:0 25px}.static-cdn{position:absolute;bottom:0;left:0;height:40px;opacity:.8}.static-cdn .span-tname{background-color:#eee;display:inline-block;height:22px;line-height:22px;float:left;padding:0 5px;border:#ddd 1px solid;color:#777;margin-left:40px;font-size:12px}.static-cdn select{border:1px solid #ddd;height:24px;line-height:22px;padding-left:5px;margin-left:-1px;color:#777;font-size:12px}.btlink{text-decoration:none;color:#20a53a}.scan_tips{height:25px;margin-top:10px;text-align:center}.scan_tips img{width:25px;height:25px;margin-right:10px;margin-left:-5px}.scan_tips span{display:inline-block;font-size:18px;line-height:25px;vertical-align:top;font-weight:600;color:#555} \ No newline at end of file diff --git a/BTPanel/static/css/site.css b/BTPanel/static/css/site.css index c6e95713..f87fd312 100644 --- a/BTPanel/static/css/site.css +++ b/BTPanel/static/css/site.css @@ -5,7 +5,7 @@ @WebSite: bt.cn */ - body { +body { line-height: 1.4; color: #333; font-family: "微软雅黑", Arial, Helvetica, sans-serif; @@ -29,6 +29,7 @@ h6, p, ul, ol, +label, form { margin: 0 } @@ -81,10 +82,10 @@ input::-webkit-input-placeholder { color: #999 } -input[type=checkbox], input[type=radio]{ +input[type=checkbox], input[type=radio] { height: 14px; - width: 14px; - margin-top: 0; + width: 14px; + margin-top: 0; } body, @@ -159,30 +160,37 @@ html { .bgw { background-color: white } -.move_class{ - float:right; + +.move_class { + float: right; } + .bge6 { background-color: #f2f2f2; } + /* .bge6 { background-color: #e6e9ee } */ - .btn{ - vertical-align: inherit; - } - .file-box .btn{ - vertical-align: middle; - } -.file-box .page{ - border:none; +.btn { + vertical-align: inherit; } -.file-box .Pcount-item{ - border-right:1px solid #ececec; + +.file-box .btn { + vertical-align: middle; } -.file-box{ - padding-bottom: 1px; + +.file-box .page { + border: none; +} + +.file-box .Pcount-item { + border-right: 1px solid #ececec; +} + +.file-box { + padding-bottom: 1px; } .plr10 { @@ -198,11 +206,11 @@ html { } .ptb10 { - padding: 10px 0 + padding: 10px 0 } .ptb15 { - padding: 15px 0 + padding: 15px 0 } .ptb20 { @@ -277,6 +285,10 @@ html { margin: 0 20px } +.mb0 { + margin-bottom: 0 !important; +} + .line .mb0 { margin-bottom: 0 } @@ -340,11 +352,21 @@ html { cursor: help } -.limit-text-length{ +.limit-text-length { display: inline-block; overflow: hidden; text-overflow: ellipsis; - white-space: nowrap; + white-space: nowrap; +} + +.clear { + clear: both; +} + +.text-overflow { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .btlink { @@ -365,12 +387,12 @@ html { border-color: orange; color: orange } -.input-edit{ +.input-edit { cursor: pointer; - border:transparent 1px solid; - min-height: 20px; - min-width: 150px; - display: inline-block; + border: transparent 1px solid; + min-height: 20px; + min-width: 150px; + display: inline-block; } .input-edit:hover{ border:#ccc 1px solid; @@ -391,20 +413,20 @@ html { } .important-title { - color:red; + color: red; background-color: #fef3e2; - line-height: 20px; - margin-bottom: 15px; - padding-left: 10px; - box-shadow: 0 1px 2px 0 rgba(0,0,0,.2); - padding: 10px; + line-height: 20px; + margin-bottom: 15px; + padding-left: 10px; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .2); + padding: 10px; } .info-title-tips { background-color: #fbfbfb; - border: 1px solid #eee; - line-height: 46px; - margin-bottom: 15px; - padding-left: 10px; + border: 1px solid #eee; + line-height: 46px; + margin-bottom: 15px; + padding-left: 10px; } .bt-input-text { @@ -789,45 +811,45 @@ html { margin-top:50px; } .sidebar-scroll-panel { - width: 100%; - z-index: 100; - height: 50px; - position: fixed; + width: 100%; + z-index: 100; + height: 50px; + position: fixed; } .sidebar-scroll-panel .sidebar-auto { - height: 100%; - margin: 0 15px; - background-color: #fff; - box-shadow: 0 0 10px #ddd; + height: 100%; + margin: 0 15px; + background-color: #fff; + box-shadow: 0 0 10px #ddd; } .sidebar-scroll-panel .mypcip{ display:none; } .sidebar-scroll-panel .sidebar-auto .menu { - background-color: #fff; - height: 50px; - border-bottom: #20a53a 1px solid; + background-color: #fff; + height: 50px; + border-bottom: #20a53a 1px solid; } .sidebar-scroll-panel .menu li { - margin-bottom: 1px; - position: relative; - float: left; + margin-bottom: 1px; + position: relative; + float: left; } .sidebar-scroll-panel .menu li a { - font-size: 14px; - display: block; - line-height: 48px; - padding-left: 41px; - background-repeat: no-repeat; - background-size: 16px auto; - background-position: 21px 17px; - border-right: #eee 1px solid; - padding-right: 26px; + font-size: 14px; + display: block; + line-height: 48px; + padding-left: 41px; + background-repeat: no-repeat; + background-size: 16px auto; + background-position: 21px 17px; + border-right: #eee 1px solid; + padding-right: 26px; } .sidebar-scroll-panel .menu li.current a,.sidebar-scroll-panel .menu li a:hover { - background-color: #20a53a; - color: #fff; - border-bottom: #20a53a 2px solid; + background-color: #20a53a; + color: #fff; + border-bottom: #20a53a 2px solid; } .sidebar-scroll-panel .btpc-plus{ display:none; @@ -876,8 +898,8 @@ html { line-height: 50px; padding-left: 30px; white-space: nowrap; - max-width: 146px; - overflow: hidden; + max-width: 146px; + overflow: hidden; } .sidebar-scroll .btpc-plus { @@ -1067,6 +1089,15 @@ html .menu .menu_control:hover { html .menu .menu_firewall:hover { background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP////39/fv7+/f39+3t7evr6+np6efn5+Xl5ePj4+Hh4d/f393d3dnZ2dfX19XV1dHR0cvLy8nJycfHx8PDw8HBwb+/v729vbu7u7m5ube3t7W1tbOzs7Gxsa+vr6enp6WlpaOjo5+fnwDZNpeXl42NjYuLi4mJiSitSCqpSiypSoeHhyirSC6lTIWFhTKhTjiZUDyXUnx8fECRVEKPVHp6enh4eEqHWEiHWHR0dFB+XHBwcFpyYGZmZgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFAwA9ACwAAAAADwAQAAAGdMCesEeiHAACR2c17J0oiUXEE/pgHAsFptYjQFzN4c6UmPQKoXAYIzl/1M1M5AyCDzdzwtveu8wRdXwXbQ8afD0SGD0dEIcNIj01CDl2NQeUPRZzcA8WTQWKYRUGYTkJDlw9MgwLO3AaBh0bBhyHJQEDYE1BACH5BAUEAD0ALAMABgACAAMAAAYHwB6PN+qlggAh+QQFBAA9ACwFAAcAAQADAAAGBUDaCBUEACH5BAUIAD0ALAYACAACAAMAAAYHwBtuNGKpggAh+QQFCAA9ACwIAAcAAQADAAAGBcDZSBUEACH5BAUIAD0ALAkABgABAAMAAAYFwNioFQQAIfkEBQgAPQAsCgAFAAEAAwAABgVA2OgVBAAh+QQFBgA9ACwLAAQAAgADAAAGB0Aab6SD9YIAIfkECTIAPQAsAAAAAA8AEAAABhXAnnBILBqPyKRyyWw6n9CodEqtJoMAOw==") } + +.menu .menu_btwaf,.menu .current .menu_btwaf{ + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAGTSURBVDiNpZNBaxNBGIaf3RkSSEkJLmYIybYyETRG6CEwELAevAheBH9CKRVksHdPgRxzacBDwT/hT8hBydEK0aqHXFI9BCKBaEJSttuDadjgtpj63uab733ebz4YJwxD4tRsNm+k0+nXWmuRSqVeGGMGcX2iVqstDkopx/O8Xcdxbna73Tuu675SSt0NguBjq9XamEwmj3zf/wAsUp0wDGk0GjvFYvFeoVB42263D33fT/b7/RNgu1wuI6VsD4fDnOd5p51O53kul3uazWa/VCqVNxIgkUgcDAYDORqNHgMbvV4vLYS4LaW8CHqQyWQAfmutD4Fbs9nsDPgDEEL8nE6nm+Px+L4QInYnAEEQrCWTydLc8wPAnd+58+Kl5hjJvwArSkSNK0VHQ/9ngiXA2jUA61HAp2sAOlHAM+DzCuZj4MkCYK39DuwBo38w/wL2jDEn0Qmw1r4HHgJfrzB/E0JsV6vVdxeFpe1ba4+ALcCy/KRj4CWwZYw5inqcy75zvV5fz+fzB1proZTaL5VKw7i+cyFObUDzXeUJAAAAAElFTkSuQmCC"); +} + +html .menu .menu_btwaf:hover{ + background-image: url("data:image/gif;base64,R0lGODlhEAAQAPcAAMbGxsLCwrLGsr6+vrLCsrq6uq6+rqLCop7Cnqq6qqa6pp68npq+mrKysqq2qpq6mo6+joq+ipq2mq6urqKyooq6ipa2loa6hqKuonm+eYq2ip6unnW+dZKyknm6eZKukoa0hqampnW6dZqqmoKygnm2eXW1daKiooauhnG2cY6qjoKugmW6ZZamlmm2aWG6YYqmimW2ZXWudWG2YWmyaXGucVG6UX2ofZqamlW2VXGqcXmmeWWvZVG2UY6ejk22TVmyWVWyVWWqZZaWloKeglGyUXGmcVmuWUW2RW2mbWGqYVWuVUmySUG2QV2qXT22PVmqWYqWipKSkkWyRUmuSYaWhnGecWWiZVGqUT2yPY6Ojk2oTYaShnmWeVmiWYKSgkGqQWmaaX2SfXGWcW2WbYqKioKOgnmSeXWSdWmWaX2OfUGmQVmcWWGYYXGScU2eTX2KfYaGhmmSaV2WXWWSZXGOcXGKcVGWUYKCgl2SXXmGeWWOZU2WTVmSWW2KbUGaQVWSVUmWST2aPWWKZXWCdU2STW2GbWGKYVmOWUGWQVWOVX19fUmSST2WPTGaMW2CbVmKWWGGYU2OTWWEZUmOSUGSQXV9dVGKUUWORXl5eTGWMT2SPU2KTTmSOS2WLWGCYUGOQVWGVV2CXTWSNUmKSWl9aUWKRTGSMVGGUXF5cT2OPVmCWUGKQXV1dS2SLTmOOWF9YW15bUmGSVGCUWV5ZVWAVVl9WWl1aXFxcW1zbV15XWV1ZWF1YVl5WWlxaW1tbWVxZU11TVltWVFwUVVtVWVlZV1pXUFxQU1tTWFlYVVpVUVvRUltSVllWU1pTVFnUV1hXVVhVUllSV1dXVlfWT1pPU1hTUVlRUlhSUFlQUVhRVFdUU1dTUldSVVZVUFfQVFYUUVdRVVVVU1ZTUlZSU1VTUFYQVFRUUVXRUlVSU1RTT1VPU1NTUVRRUFRQUlOST1RPUFNQUlJSUVJRUFJQUVFRTlHOT1FPUFBQT1BPTlBOT09PTk9OTU9NTk5OTMzMyH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgD/ACwAAAAADwAQAAAIigD/CRzoL5OUfQMTCpw27ZyUAcXO/UoorpU4PDgWDQlwIkQDdotw/SszREsZKVI2AgAQIESBAP/ioJypcuXKAiNnoqxpc0BOnTxX+jwJNIBNoT9pGj0KU4vOlEuP/lv0NCiACf/24Sl6dEI9gfKc7ow6QFzCejKhrmxwTuFCPBsniHSbsFUZhAkDAgAh+QQFCgD/ACwAAAAAAQABAAAIBAD/BQQAIfkEBQoA/wAsAAAAAA8AEAAACHUA/wkc+C8VF4IICVYxkJAgIR+EFrZoQeGfJYFqophR84VLFAMCBBAYkYAhHC4duXgEGTJkgn8nVar82NIlTJkzWbY0iXNlTQE8cdKsydBMz6E1/xE6qjMkBoF6hDZ9OtBozp0Jo/oUULHhPz0fqXoVGMtMwoAAIfkEBQoA/wAsAAAAAA8AEAAACHQA/wkc+K+UGIIICXZhkFBgqX+GiDxaCANGh3+PBNbpgqYOGjQLEYhUYYGhnY8oQ4oU+eDfSZQgGaxk+c8jTJUrGdpMKXPmgpowY85E8DOo0JkHMAbFKfKDQEM3ezYluBMnQ4R+Pqq82BDiQqddBfqjdSZhQAAh+QQFCgD/ACwAAAAADwAQAAAIeAD/CRz479MYgggJprmQUOCnf5GsRFp4YweJf5ME7iEjZw8dOQsjiLxxIcK/QXI+ygFZUqRIDf/2rJwZ0mWECjFnrqzpEqdMnTxF+tTJ0qbIfypptnQJASLRoBEu/jsEdGmEFQST8sSJEGXRqA0FHlqINSxBNwkDAgAh+QQFCgD/ACwAAAAADwAQAAAIcAD/CRz4b1UYgggJzhGRkGCoK5AW6kgiQ6Cuf4jmzEEEqM8cDhlC1kjBEFGfjn08ggwZsgTGlDA/sgzp4SVMlTMz1DR5U+bMnTdx/vwXVOjMf5CC+mRZEWnPlSGbCnzKsibCpEKlJozIQWvDgmkSBgQAIfkEBQoA/wAsAAAAAA8AEAAACHEA/wkc+A9VG4IICfJ5kVBgqH+cvFxaKMQJjX+QBFLic4cSI0YLWYhUMoPFP0kfU4YUKdLFyZQfV7KM8RKmTJY1Vb5giROlzZ08TRaCCRIoT4hEb7K4mLMoTx4Eh8Y0ahKhz5VQG55cyMOW1oGzDiIMCAAh+QQFCgD/ACwAAAAADwAQAAAIdAD/CRz4jxQbgggJNuqRkCCrLZga2YCCBQhBVX8Cqeq0qdEPGyCX9GAIahPHTR0/ggSZ418llDA9rmTpEiZKmTNb2rypcmXLlzZxzvy3M+VMkP8w7RQK0uK/kjF72nAqMGhPhgih4gzSUCAoj1S7/pN1EGFAACH5BAUKAP8ALAAAAAAPABAAAAhvAP8JHPgP1BuCCAlqepJQIKV/r9ao8vRkCxgm/woJPOVI0ClXrig+edJkCsl/o0CqFDlyJBKUKkGybPkyZcyZLmHebNmyyb+PO3meBLpS6EiIMUMaxYi0KE+mG52O9InQJkuoCV9RxNrwH6uDCAMCACH5BAUKAP8ALAAAAAABAAEAAAgEAP8FBAAh+QQFCgD/ACwAAAAADwAQAAAIeQD/CRz4jxQbgggJNuqRUOClf6y2YGpkAwoWIP8UCVT1J5CqTpsa/bBBckkPhqA2gdwUciRJkjn+VWJJU+RLmDJpsrR5M6bOnS5fxpypk+fNfz9b3iT5D9NPoyQx/ktZM6gNqQKLBmWIkCrPIA0FghKJNew/WQcRBgQAIfkEBQoA/wAsAAAAAA8AEAAACGwA/wkc+A9VG4IICfJ5kZAgJy+XFgpxQoMgJT53KDFitJCFRyUzWPyTtLFkR48eXYwsufEkyhgrWbpEGdPkC5Q0Scq8iVNkIZYceeL8xwnoTBYVawbFyYPgz5ZCRSLUebJpw5ELrV4VOOsgwoAAIfkEBQoA/wAsAAAAAA8AEAAACHcA/wkc+G9VGIIICc4RkVCgqH+hrkBaqCOJjH+HBCKaMwcRoD5zOGQYWSMFQ0R9PvYBKXLkyBL/UK5cGdLlSA8xZ9Js6RKnzJk1bfrUydJmBpxEixr9B4loUJcXmep8miGqwKk8cSJsqtRqwokcZMBqSHBVmoQBAQAh+QQFCgD/ACwAAAAADwAQAAAIcwD/CRz479MYgggJprmQkGAkK5EW3thBQiCtf3vIyNlDR87CCCBvXIjwb5CcjnI8jgQJUgPGlDA/soxQ4SVMlTNp2oy5kmXNPTdx5vyHkmdOCP8iBZXJsuK/QzeZRlhBsCjTmghNCnXa8NBCqg0JwnKTMCAAIfkECQoA/wAsAAAAAA8AEAAACHIA/wkc+K+UGIIICXZhkFBgqX+GiDxaCANGh3+PBNbpgqYOGjQLEYhUYYGhnY8oQ4oU+eDfSZQgGaxk+c8jTJUrGdpMKXPmgpowY85E8DOo0JkHMAbFKfKDQEM3ezYluBMnQ4R+Pqq82BDiQqddCZ5JGBAAIfkEBQoA/wAsAAAAAA8AEAAACIsA/wkcyC8Vl3wDEwqkBq1cFQPJwPlKCC6VN0I+CD1s0YLCO0u5/qmJYkbNFy5RDAgQQGBEAgP/4HA5yQWlypUrE8SsyTMlzpw7edr8KQCmTKE+fxoVOlTpPzNMk/78RyjqTZwY/uXTg/SqAAz1BL6DWlOqAW8J53FtSkGdwoV6UmII+TZhLDP7FAYEACH5BAkKAP8ALAAAAAAPABAAAAh1AP8JHPgvkxSCCAlKGZCQIB4ci4YEOBGiwb9FAssM0VJGihSJAAAECFEgwL84HlOCDBmywL+OKT8GYBmSIUyVM2najCmTJoCdMVeyNKmFp1CWF43mZDlBIJ6gSwE0HVjUo1CGCFH2BGCx4T88Eqd6FdiqTMKAADs="); +} + html .menu .menu_xterm:hover { background-image: url("data:image/gif;base64,R0lGODlhEAAQAMQXAJ6ipUFITySRPIiMkDFRQuXm5jJKQy1kQCGeOzBXQSl3PiWLPVhfZSxrQCpxP9na2yeEPTREQ8LExmRqb5OXm/Hx8TU9RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjMyQjYwMTU3OTA2QTExRUJCN0NFQUUwQjRDMzM4RDJDIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjMyQjYwMTU4OTA2QTExRUJCN0NFQUUwQjRDMzM4RDJDIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MzJCNjAxNTU5MDZBMTFFQkI3Q0VBRTBCNEMzMzhEMkMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MzJCNjAxNTY5MDZBMTFFQkI3Q0VBRTBCNEMzMzhEMkMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFMgAXACwAAAAAEAAQAAAFU6AljmRpBVOqrqtFVXAsy9Q7VMMDzHHN/5UawGQC1CiWg4IoOiINAoGB6BxBENSjyIE4oFKMprYhIFgYkvTQpWWOqm6RcVBI2+/2AuXL6k8CcSYhACH5BAUyABcALAcACQAFAAIAAAUIYJSMBLGcSggAIfkEBTIAFwAsBwAJAAUAAgAABQhgMI0MI51ACAAh+QQFMgAXACwHAAkABQACAAAFCGCUjASxnEoIADs=") } @@ -1091,10 +1122,19 @@ html .menu .menu_exit:hover { background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAOPj4+Hh4dnZ2dXV1c3NzcvLy8fHx8PDw8HBwb+/v729vbu7u7W1ta2trampqaenp6Ojo6GhoZ2dnQzNOgjPOpeXlxq9Qhi9QI+Pj4uLi4mJiSatSCqpSjCjTDSfToGBgTadTjKfTjibUDqZUDqXUDyVUkCRVESNVnZ2dkyFWnR0dE6BWlB+XFB8XFR4Xlh2XlxwYF5uYmZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCAAyACwCAAEACwAOAAAGQMBGgUAsEgyoAEMSaTYlgwzgI6taZYkKQHO1ZlDb7jUsrpLLZ3G6ux5zxRgwVayoBBQPh17/EGQgCQiCgwcLKkEAIfkEBQgAMgAsBwAHAAEAAgAABgRA0ykIACH5BAUIADIALAgABwABAAIAAAYEwI4oCAAh+QQFCAAyACwJAAcAAgACAAAGBkCPhzQKAgAh+QQFCAAyACwLAAcAAQACAAAGBEDPKAgAIfkEBQgAMgAsDAAHAAIAAgAABgZA0KbECQIAIfkEBQgAMgAsDAAFAAIAAgAABgZAVypmCQIAIfkEBQgAMgAsDgAGAAIAAwAABgfAlYySmqyCACH5BAUIADIALAwACQADAAEAAAYFQNiFFQQAIfkEBQgAMgAsDAAKAAIAAQAABgTAFysIACH5BAkyADIALAIAAQAOAA4AAAYUQJlwSCwaj8ikcslsOp/QqHRqDAIAOw==") } +html .menu .menu_docker { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjQ2OUMyQTlEQzA3RjExRUNBRjc0QjI4QkM0QUMxRDBBIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjQ2OUMyQTlFQzA3RjExRUNBRjc0QjI4QkM0QUMxRDBBIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NDY5QzJBOUJDMDdGMTFFQ0FGNzRCMjhCQzRBQzFEMEEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NDY5QzJBOUNDMDdGMTFFQ0FGNzRCMjhCQzRBQzFEMEEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz606BQhAAAAn0lEQVR42mL8//8/AyWAiYFCQB8DZs2a9R+EsckxUhoGLPhshVkCxGB2WloaI4ZCZBeA2LgwMpg5c+Z/rF7AZisyOz09nXH69OkNTExM9f/+/QtiZGQ0BQcikAHGxIDMzMwGcOgzMa0D6qkEuwBXCOMDINeQnQ5gmskyAOj3GIx0API/csjiAkB1ZsCoPI3VAPTowedsqqZEijMTQIABAO7NWyhHRpXVAAAAAElFTkSuQmCC"); +} + +html .menu .menu_docker:hover { + background-image: url("data:image/gif;base64,R0lGODlhEAAQAJEDAJmZmSClOpiYmJmZmSH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkIzQURGQkRFQzA3RTExRUNBNUIyOUQ0OTNDNTk5MjFEIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkIzQURGQkRGQzA3RTExRUNBNUIyOUQ0OTNDNTk5MjFEIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QjNBREZCRENDMDdFMTFFQ0E1QjI5RDQ5M0M1OTkyMUQiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QjNBREZCRERDMDdFMTFFQ0E1QjI5RDQ5M0M1OTkyMUQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFHgADACwAAAAAEAAQAAACHZyPqcvtFsKbtII6rgWX+/8d4KgZZJicZTpi7lIAACH5BAUeAAMALAYABQAFAAEAAAIDTGYFACH5BAUeAAMALAMABwAIAAIAAAIFTGZol1YAOw==") +} + .pos-box { height: 50px; width: 100%; - box-shadow: 0 1px 2px 0 rgba(0,0,0,.1); + box-shadow: 0 3px 2px 0 rgba(0, 0, 0, 5%) !important; + border-radius: 4px; } .conter-box{ @@ -1104,10 +1144,10 @@ html .menu .menu_exit:hover { .position { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFZSURBVDiNpdO9a1VBEAXw3zOioIUQEdFGkQdWVtr6D1ikERtRppLU6awUsUkh2CgoBNwhoPhsLESxERR7wcI2XSBNMAHRNFmLbB6Xa64RXFjYOXvmzMfOjmqt/mcd2I+QmUuZOcgbDWWQmTN4iFms4wGu4jC+RMT7wQwy8xBe4FtE3MBXPMdqE5sfLCEzj+Il3kXEE4iIJTzFFbzB9z0FMvMYJphExLPuXUQs4y1eYfmPHmTmbHN+FBGv92zMDm+ulXAzItZHtVaZeRH3sBgRn4ecOyKXcRt3dkuYwyJOZeaJfZyP42TjX1Nrne5SyqSUMu5ho559tpQy2bUP9gL8wHYnzbtYzczTuB8RHzs8/H0Sz+ETbuEDxg3f7pL6AhW/2vknNiNiCxvY6uDT8e2XMINLmbmC89js3I0z8wLOdP36Aht43KIcwULD1+w82/WGT+dk8DP96/oNlqecb6uu8YEAAAAASUVORK5CYII="); background-position: 17px 13px; - background-repeat: no-repeat; - background-size: 20px auto; - line-height: 50px; - padding-left: 30px; + background-repeat: no-repeat; + background-size: 20px auto; + line-height: 50px; + padding-left: 30px; } .search { @@ -1142,39 +1182,39 @@ html .menu .menu_exit:hover { line-height: 50px; border-bottom: 1px solid #eee } -.title.tabs-nav{ - padding:0; - position: relative; +.title.tabs-nav { + padding: 0; + position: relative; } -.title.tabs-nav select{ - position: absolute; - right: 5px; - top: 50%; - margin-top: -12px; +.title.tabs-nav select { + position: absolute; + right: 5px; + top: 50%; + margin-top: -12px; } -.tabs-content{ - height: 442px; +.tabs-content { + height: 442px; } -.tabs-content .tabs-item{ - display: none; - height: 100%; - width: 100%; - background-color: #fff; +.tabs-content .tabs-item { + display: none; + height: 100%; + width: 100%; + background-color: #fff; } -.tabs-content .tabs-active.tabs-item{ - display: inline-block; +.tabs-content .tabs-active.tabs-item { + display: inline-block; } -.title.tabs-nav span{ - padding: 0 25px; - height: 100%; - cursor: pointer; - display: inline-block; +.title.tabs-nav span { + padding: 0 25px; + height: 100%; + cursor: pointer; + display: inline-block; } -.title.tabs-nav span.active{ - border-bottom: 2px solid #20a53a; +.title.tabs-nav span.active { + border-bottom: 2px solid #20a53a; } .title h3 { @@ -1183,8 +1223,8 @@ html .menu .menu_exit:hover { margin-right: 12px } .tablescroll { - overflow: auto; - border: #e6e6e6 1px solid; + overflow: auto; + border: #e6e6e6 1px solid; } .divtable .table { border: 1px solid #ddd; @@ -1209,12 +1249,12 @@ html .menu .menu_exit:hover { color: #666; font-weight: normal; padding: 8px 10px; - font-size: 12.5px; + font-size: 12.5px; } -.divtable .table_toolbar{ - left: 8px; - bottom: 6px; - position: absolute; +.divtable .table_toolbar { + left: 8px; + bottom: 6px; + position: absolute; } .table .btlink{ word-break: break-word; @@ -1253,24 +1293,24 @@ html .menu .menu_exit:hover { border-top: none; margin: 0; } -.site_type{ - height: 30px; - line-height: 30px; - bottom: 0; - position: absolute; +.site_type { + height: 30px; + line-height: 30px; + bottom: 0; + position: absolute; } -.site_type span{ +.site_type span { display: inline-block; - height: 30px; - line-height: 30px; - padding: 0 10px; - font-family: "Arial"; - font-size: 13px; - color: #666; - float: left; - border: 1px solid #ececec; - border-right: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + font-family: "Arial"; + font-size: 13px; + color: #666; + float: left; + border: 1px solid #ececec; + border-right: none; /* border: 1px solid #ececec; */ } .site_type select{ @@ -1300,15 +1340,15 @@ html .menu .menu_exit:hover { .page span, .page a { display: inline-block; - height: 28px; - line-height: 28px; - padding: 0 10px; - font-family: "Arial"; - font-size: 13px; - color: #666; - float: left; - border: 1px solid #ececec; - border-right: none; + height: 28px; + line-height: 28px; + padding: 0 10px; + font-family: "Arial"; + font-size: 13px; + color: #666; + float: left; + border: 1px solid #ececec; + border-right: none; } .page a.prev { @@ -1345,9 +1385,9 @@ html .menu .menu_exit:hover { .page .Pcount { color: #333; - padding: 0 10px; - margin-right: 0px; - border-right: 1px solid #ececec; + padding: 0 10px; + margin-right: 0px; + border-right: 1px solid #ececec; } .system-info-con { @@ -1512,12 +1552,12 @@ html .menu .menu_exit:hover { .mem-re-min { background: url(/static/img/ico/rocket_min.png) no-repeat center center; height: 35px; - left: 35px; - position: absolute; - top: 2px; - width: 20px; - z-index: 9; - opacity: .9; + left: 35px; + position: absolute; + top: 2px; + width: 20px; + z-index: 9; + opacity: .9; } .mem-re-con { @@ -1663,10 +1703,10 @@ html .menu .menu_exit:hover { .bw-info{ height:80px; } -.bw-info > div{ - float: left; - padding-top: 35px; - text-align: center; +.bw-info > div { + float: left; + padding-top: 35px; + text-align: center; } @@ -1681,24 +1721,24 @@ html .menu .menu_exit:hover { .bw-info .ico-down, .bw-info .ico-up, .bw-info .ico-write, -.bw-info .ico-read{ - width:10px; - height:10px; - display:inline-block; - margin-right:3px; - border-radius: 5px; +.bw-info .ico-read { + width: 10px; + height: 10px; + display: inline-block; + margin-right: 3px; + border-radius: 5px; } -.bw-info .ico-down{ - background-color:#52a9ff; +.bw-info .ico-down { + background-color: #52a9ff; } -.bw-info .ico-up{ - background-color:#f7b851; +.bw-info .ico-up { + background-color: #f7b851; } -.bw-info .ico-write{ - background-color:#6CC0CF; +.bw-info .ico-write { + background-color: #6CC0CF; } -.bw-info .ico-read{ - background-color:#FF4683; +.bw-info .ico-read { + background-color: #FF4683; } .bw-info ul li { height: 56px; @@ -1748,7 +1788,9 @@ html .menu .menu_exit:hover { .line .info-r { margin-bottom: 5px; margin-left: 125px; - position: relative + position: relative; + min-height: 30px; + line-height: 30px; } .placeholder { @@ -1806,7 +1848,7 @@ html .menu .menu_exit:hover { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; - padding-right: 5px; + padding-right: 5px; } .bt-w-menu p a { @@ -1819,24 +1861,29 @@ html .menu .menu_exit:hover { .bt-w-con { position: relative; + height: 100%; margin-left: 170px; - padding: 7px 15px; } -.bt-w-con.webedit-con { + +.bt-w-con .bt-w-item { + height: 100%; + display: none; +} + +.bt-w-con .bt-w-item.active { + display: block; +} + +.webedit-con { height: 100%; overflow-y: auto; } -.bt-w-con .bt-w-item{ - height: 100%; - display:none; -} -.bt-w-con .bt-w-item.active{ - display:block; -} -.taskcon{ + +.taskcon { height: 500px; - overflow: auto; + overflow: auto; } + .mes_channel .bt-w-con { margin-left: 110px; } @@ -2088,8 +2135,8 @@ html .menu .menu_exit:hover { .rec-install h3 { font-size: 20px; - margin-bottom: 10px; - margin-left: 5px; + margin-bottom: 10px; + margin-left: 5px; } .rec-box-con { @@ -2114,10 +2161,10 @@ html .menu .menu_exit:hover { } .fangshi { - line-height: 19px; - margin-top: 10px; - display: block; - margin-bottom: 15px; + line-height: 19px; + margin-top: 10px; + display: block; + margin-bottom: 15px; } .fangshi1 { margin-top: 20px; @@ -2128,8 +2175,8 @@ html .menu .menu_exit:hover { } .fangshi label { font-weight: normal; - margin-right: 30px; - margin-left: 10px; + margin-right: 30px; + margin-left: 10px; } .fangshi1 label { font-weight: normal; @@ -2144,11 +2191,11 @@ html .menu .menu_exit:hover { height: 18px; vertical-align:-8px; } -.fangshi1 .bt_checkbox_group.active:after{ +.fangshi1 .bt_checkbox_group.active:after { width: 10px; - height: 5px; - margin-left: -5px; - margin-top: -3.5px; + height: 5px; + margin-left: -5px; + margin-top: -3.5px; } .fangshi label input { vertical-align: -2px; @@ -2190,18 +2237,18 @@ html .menu .menu_exit:hover { margin-top:-6px; transition: all 400ms; } -.bt_select_ul{ +.bt_select_ul { border: 1px solid #d2d2d2; padding: 5px 0; border-radius: 2px; - box-shadow: 0 2px 4px rgba(0,0,0,.12); + box-shadow: 0 2px 4px rgba(0, 0, 0, .12); position: absolute; - z-index: 9999; - background: #fff; - left: -1px; - right: -1px; - top: 29px; - border-radius: 2px; + z-index: 9999; + background: #fff; + left: -1px; + right: -1px; + top: 29px; + border-radius: 2px; display: none; } .bt_select_ul.active{ @@ -2225,14 +2272,14 @@ html .menu .menu_exit:hover { background: #1b8c31; border-color: #1b8c31; } -.bt_checkbox_group{ +.bt_checkbox_group { display: inline-block; - height: 20px; - width: 20px; - border-radius: 2px; - cursor: pointer; - border: 1px solid #c2c2c2; - position: relative; + height: 20px; + width: 20px; + border-radius: 2px; + cursor: pointer; + border: 1px solid #c2c2c2; + position: relative; text-align: center; line-height: 20px; margin: 4px; @@ -2242,21 +2289,21 @@ html .menu .menu_exit:hover { background: #20a53a; border-color: #20a53a; } -.bt_checkbox_group.active:after{ +.bt_checkbox_group.active:after { content: ''; - display: inline-block; - width: 13px; - height: 6px; - border-width: 0px; - border-left-width: 2px; - border-bottom-width: 2px; - border-color: #fff; - border-style: solid; - transform: rotate(-45deg); - -webkit-transform-origin: center center; + display: inline-block; + width: 13px; + height: 6px; + border-width: 0px; + border-left-width: 2px; + border-bottom-width: 2px; + border-color: #fff; + border-style: solid; + transform: rotate(-45deg); + -webkit-transform-origin: center center; transform-origin: center center; position: absolute; - top:50%; + top: 50%; left: 50%; margin-top: -4px; margin-left: -6.5px; @@ -2581,68 +2628,68 @@ html .menu .menu_exit:hover { } .changepath .path-top .place span { - color: #444; - font-size: 12px; - font-weight: bold; - display: inline-block; - width: 450px; - white-space: pre; - text-overflow: ellipsis; - overflow: hidden; - vertical-align: middle; + color: #444; + font-size: 12px; + font-weight: bold; + display: inline-block; + width: 450px; + white-space: pre; + text-overflow: ellipsis; + overflow: hidden; + vertical-align: middle; } .changepath .path-con-left { - float: left; - width: 150px; - height: 450px; - border-right: #aaa 1px solid; - padding: 12px 0; - overflow-y: auto; + float: left; + width: 150px; + height: 450px; + border-right: #aaa 1px solid; + padding: 12px 0; + overflow-y: auto; } .changepath .path-con-left dl { - width: 100%; - height: 100%; - margin-bottom: 0; - padding-left: 12px; + width: 100%; + height: 100%; + margin-bottom: 0; + padding-left: 12px; } .changepath .path-con-left dl dt { - width: 100%; - height: 100%; - padding-left: 22px; - background: url("../img/ico-computer.png") no-repeat left top; - font-size: 14px; - font-weight: normal; + width: 100%; + height: 100%; + padding-left: 22px; + background: url("../img/ico-computer.png") no-repeat left top; + font-size: 14px; + font-weight: normal; } .changepath .path-con-left dl dd { - width: 100%; - line-height: 16px; - padding-left: 6px; - padding-right: 6px; - cursor: pointer; + width: 100%; + line-height: 16px; + padding-left: 6px; + padding-right: 6px; + cursor: pointer; } .changepath .path-con-left dl dd + dd { - margin-top: 12px; + margin-top: 12px; } .changepath .path-con-left dl dd span { - color: #666; + color: #666; } .changepath .path-con-left dl dd span:first-child { - display: inline-block; - margin-right: 6px; + display: inline-block; + margin-right: 6px; } -.changepath .path-con-right{ - float: left; - width: 530px; - height: 450px; - overflow: auto; +.changepath .path-con-right { + float: left; + height: 450px; + overflow: auto; + width: 530px; } .changepath .path-con-right .default li { @@ -2660,7 +2707,8 @@ html .menu .menu_exit:hover { .path-con-right .file-list { display: none; - height:450px; + height: 450px; + overflow: auto; } .list-list tr:last-child{ @@ -2671,12 +2719,12 @@ html .menu .menu_exit:hover { margin-top: 6px; } -.list-list .bt_open_dir span:nth-child(2){ +.list-list .bt_open_dir span:nth-child(2) { width: 155px; - display: inline-block; - text-overflow: ellipsis; - overflow: hidden; - white-space: pre; + display: inline-block; + text-overflow: ellipsis; + overflow: hidden; + white-space: pre; vertical-align: middle; } .path-con-right .list-list td { @@ -2857,6 +2905,12 @@ html .menu .menu_exit:hover { list-style-type: decimal } +ul.cmdlist { + list-style-type: decimal; + height: 480px; + overflow-y: auto; +} + .tab-con ul.cmdlist li { position: relative; list-style-type: decimal; @@ -2959,11 +3013,12 @@ html .menu .menu_exit:hover { } .footer { - bottom: 0; position: absolute; text-align: center; - width: 100%; - line-height: 50px + left: 180px; + right: 0; + bottom: 0; + line-height: 50px; } .bingfa { @@ -3153,21 +3208,18 @@ html .menu .menu_exit:hover { .table>tbody>tr>td { vertical-align: middle; padding: 5px 10px; - height: 40px; + height: 40px; box-sizing: border-box; } .databaseImportTable>tbody>tr>td { vertical-align: middle; padding: 5px 10px; - height: 40px; + height: 40px; box-sizing: border-box; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; } -.table>tbody{ - border-bottom: 1px solid #ddd; -} .showpicdiv { max-height: 500px; overflow: hidden; @@ -3512,7 +3564,7 @@ html .menu .menu_exit:hover { } .list-list .ico-gz, .list-list .ico-bz2 { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAHtSURBVFiF7Ze/T8JAFMe/1QYCRKIDIe1AYcAfLJLowL/grCbGydEJTXBw0qiLA4NxNGFlkX/A0UlJJGEBQzoAC3EzxhgTgz0HPXI9jrahxTr4TZrm3r3r+/Tdu2tPIoTAT8lso9lsrgEoAVA8jLEKoMYaMpmMGABASVVVJRKJeBJZ13UAuAawmU6nayKfKa6thEIhGIbhyQUAiUTiBsC1rusrTgA8VzAY3NU07RZARQQxcQAACAQCO8lk8l4EwdeAp4pGo7QOAGDr5/4AQPoVgFgshng8brIxQM4AXisn6PdaYwHI6gJmNo6tfewe0u+1MJcvC/ueL7dH9tF+O7kqwrl82VGQiQHYZWDiAP8ZoBC+AriV7wCudkK38w/8lQxI0ve3odFoDDuoC2MXmqzMC+003gDASnZ7uVv5PgW+A5imIHK1jicA2sUjuvtLEwk49LvLngs6e4uEVaewTHjxtk5h2XTZ+u4tmg4iQ0XYPchCK9bRPcgO2lRasW7y48WOo758m5ejjWjUYKdiwXjZFiEdPOoBboJbArBvrRXrlllg0y3ytRovEUIGO1P7MPcpfbxNQRpvdfI1MPTmxAAJhI3UeXWamkw10E/lTuX23ZH0/iImkIOWADQDAITLmIRnP/vJ3BlQZYw+H8+/AFShLMorUUd8AAAAAElFTkSuQmCC") + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAHtSURBVFiF7Ze/T8JAFMe/1QYCRKIDIe1AYcAfLJLowL/grCbGydEJTXBw0qiLA4NxNGFlkX/A0UlJJGEBQzoAC3EzxhgTgz0HPXI9jrahxTr4TZrm3r3r+/Tdu2tPIoTAT8lso9lsrgEoAVA8jLEKoMYaMpmMGABASVVVJRKJeBJZ13UAuAawmU6nayKfKa6thEIhGIbhyQUAiUTiBsC1rusrTgA8VzAY3NU07RZARQQxcQAACAQCO8lk8l4EwdeAp4pGo7QOAGDr5/4AQPoVgFgshng8brIxQM4AXisn6PdaYwHI6gJmNo6tfewe0u+1MJcvC/ueL7dH9tF+O7kqwrl82VGQiQHYZWDiAP8ZoBC+AriV7wCudkK38w/8lQxI0ve3odFoDDuoC2MXmqzMC+003gDASnZ7uVv5PgW+A5imIHK1jicA2sUjuvtLEwk49LvLngs6e4uEVaewTHjxtk5h2XTZ+u4tmg4iQ0XYPchCK9bRPcgO2lRasW7y48WOo758m5ejjWjUYKdiwXjZFiEdPOoBboJbArBvrRXrlllg0y3ytRovEUIGO1P7MPcpfbxNQRpvdfI1MPTmxAAJhI3UeXWamkw10E/lTuX23ZH0/iImkIOWADQDAITLmIRnP/vJ3BlQZYw+H8+/AFShLMorUUd8AAAAAElFTkSuQmCC") } .list-list .ico-bt { @@ -3965,19 +4017,19 @@ html .menu .menu_exit:hover { .setchmod{ padding: 25px 0 75px 0; } -.setchmod .bt_tab_list{ +.setchmod .bt_tab_list { background-color: #ececec; - font-size: 0; - text-align: center; + font-size: 0; + text-align: center; } -.setchmod .bt_tab_list .bt_tab_index{ - height: 43px; - line-height: 42px; - display: inline-block; - font-size: 14px; - width: 50%; - text-align: center; - cursor: pointer; +.setchmod .bt_tab_list .bt_tab_index { + height: 43px; + line-height: 42px; + display: inline-block; + font-size: 14px; + width: 50%; + text-align: center; + cursor: pointer; } .bt_tab_list .active{ background: #fff; @@ -4218,8 +4270,9 @@ html .menu .menu_exit:hover { } .re-con-con { - height: 567px; + height: 100%; margin-left: 120px; + padding: 15px; } @media only screen and (max-width:768px) { @@ -4326,11 +4379,11 @@ html .menu .menu_exit:hover { .conf_p p{ margin-bottom: 8px; } -.conf_p span{ +.conf_p span { display: inline-block; - margin-right: 10px; - width: 140px; - text-align: right; + margin-right: 10px; + width: 140px; + text-align: right; } .btvipbox{ border: #ddd 1px solid; @@ -4369,32 +4422,32 @@ html .menu .menu_exit:hover { line-height: 32px; } /*付费插件*/ -.libLogin{ +.libLogin { width: 340px; - margin: 0 auto 20px; + margin: 0 auto; } .libLogin .bt-input-text { - width: 300px; - height: 34px; - line-height: 34px; + width: 300px; + height: 34px; + line-height: 34px; } .libLogin .line{ margin-bottom: 10px; } -.libLogin .login-button,.shuoming .login-button{ +.libLogin .login-button,.shuoming .login-button { width: 300px; - height: 36px; - 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; + height: 36px; + 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; } .libLogin .login-button:hover,.shuoming .login-button:hover{background:#10952a;background:linear-gradient(#10952a,#088d22);border:1px solid #10952a} .shuoming{ @@ -4406,29 +4459,29 @@ html .menu .menu_exit:hover { font-size: 14px; margin: 15px; } -.sm-content{ +.sm-content { border: #ccc 1px solid; - padding: 10px; - height: 200px; - background-color: #f9f9f9; - overflow: auto; + padding: 10px; + height: 200px; + background-color: #f9f9f9; + overflow: auto; } .libPay .btn-group .btn{ width: 80px; } -.libPay .btn-group .btn em{ +.libPay .btn-group .btn em { position: absolute; - right: -2px; - top: -9px; - width: 33px; - height: 19px; - background: #ff7300; - border-radius: 2px; - font-size: 10px; - line-height: 19px; - color: #fff; - transform: scale(.9, .8); - font-style: normal; + right: -2px; + top: -9px; + width: 33px; + height: 19px; + background: #ff7300; + border-radius: 2px; + font-size: 10px; + line-height: 19px; + color: #fff; + transform: scale(.9, .8); + font-style: normal; } .libPay .price{ font-size: 28px; @@ -4446,10 +4499,10 @@ html .menu .menu_exit:hover { width: 160px; margin: 10px auto 20px; } -.wxPay .ewm canvas{ +.wxPay .ewm canvas { border: #ccc 1px solid; - padding: 6px; - width: 160px; + padding: 6px; + width: 160px; } .wx-pic{ width: 160px; @@ -4457,15 +4510,15 @@ html .menu .menu_exit:hover { background: url(/static/images/weixin.png) no-repeat top center; margin-bottom: 10px; } -.wx-pay-info{ +.wx-pay-info { width: 160px; - height: 30px; - background-color: #00C800; - color: #fff; - font-size: 14px; - line-height: 30px; - text-align: center; - margin-top: 10px; + height: 30px; + background-color: #00C800; + color: #fff; + font-size: 14px; + line-height: 30px; + text-align: center; + margin-top: 10px; } .color-red{ color:red; @@ -4473,16 +4526,16 @@ html .menu .menu_exit:hover { .color-green{ color:green; } -.btpro,.btpro-gray{ - background-position: 10px center; - background-repeat:no-repeat; - padding:2px 0 2px 64px; - margin-right:5px; - cursor:pointer; - background-size: 48px; +.btpro,.btpro-gray { + background-position: 10px center; + background-repeat: no-repeat; + padding: 2px 0 2px 64px; + margin-right: 5px; + cursor: pointer; + background-size: 48px; } -.btpro{ - background-image: url('../img/pro.svg'); +.btpro { + background-image: url('../img/pro.svg'); } .btpro-gray{ background-image: url('../img/pro-default.svg'); @@ -4526,60 +4579,76 @@ html .menu .menu_exit:hover { font-size: 14px; vertical-align: middle; } -.libPay-item .li-c-item li .item-info{ - font-size: 14px; - vertical-align: middle; -} -.pay-btn-group{ - position: relative; - display: inline-block; +.libPay-item .li-c-item li .item-info { + font-size: 14px; vertical-align: middle; } -.pay-btn-group>.pay-cycle-btn { + +.voucher-group { + min-height: 80px; + max-height: 235px; + overflow-y: auto; +} + +.pay-btn-group { position: relative; - float: left; + font-size: 0; } + .pay-cycle-btn { - font-size: 0; - width: 106px; - height: 40px; - line-height: 38px; - display: block; - margin-bottom: -1px; - text-align: center; - vertical-align: middle; - border: 1px solid #ddd; - box-sizing: border-box; + position: relative; + display: inline-block; + width: 106px; + height: 40px; margin-left: -1px; - transition: border-color .1s ease-in,color .1s ease-in; + margin-bottom: -1px; + line-height: 40px; + border: 1px solid #ddd; + text-align: center; + vertical-align: top; + transition: border-color .1s ease-in, color .1s ease-in; } -.pay-cycle-btn span{ - font-size: 14px; + +.pay-cycle-btn span { + font-size: 14px; line-height: 25px; vertical-align: middle; } -.pay-btn-group>.pay-cycle-btn:first-child { - margin-left: 0; + +.pay-btn-group .pay-cycle-btn:first-child { border-top-left-radius: 4px; border-bottom-left-radius: 4px; } -.pay-btn-group>.pay-cycle-btn:nth-of-type(5n+1){ - margin-left: 0; -} -.pay-btn-group>.pay-cycle-btn:last-child { + +.pay-btn-group .pay-cycle-btn:last-child { border-top-right-radius: 4px; border-bottom-right-radius: 4px; } -.pay-btn-group .active,.li-c-item .active{ - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAMAAADzapwJAAAAA3NCSVQICAjb4U/gAAAAclBMVEX///8fpTofpTogpTogpTogpTrw+fLt+O/i8+be8uLb8d/T7djP7NXR7NbE58u548G34sCr3bWU1KGT05+FzpN+y413yIZ0x4RxxoJwxYFnwnliv3RWu2pUumhMt2FGtVxDs1k6sFE4r1Avq0cjpjwgpTpRW86VAAAAJnRSTlMAEXd3iO7//////////////////////////////////////////0c8mUAAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDUvMjkvMThc7W1eAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAHhJREFUGJVtzskSgkAMBFB2AUcR2QTcEPr/fxFmmFiW0znk8CrVac9jE1CNYqoAV8JaXTbq8K7/bBVcwRVcwRVcwbRTIDqcWrjaJ3cJ0VrdjDaHh2Sb2/F43XadPuWlTfjkJersLU2+ufNFnSdb0P/5thQv6R2CzQo9Oh+AlBF9bAAAAABJRU5ErkJggg==") no-repeat right bottom; + +.pay-btn-group .pay-cycle-btn:nth-of-type(5n+1) { + margin-left: 0; } -.pay-btn-group .pay-cycle-btn:hover{ - border: 1px solid #20a53a; - color: #20a53a; - z-index: 2; - cursor: pointer; + +.voucher-group .pay-cycle-btn:nth-of-type(5n+1) { + margin-left: -1px; } -.pay-btn-group .active,.pay-btn-group .active:hover{ + +.voucher-group .pay-cycle-btn:nth-of-type(6n+1) { + margin-left: 0; +} + +.pay-btn-group .active, .li-c-item .active { + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAMAAADzapwJAAAAA3NCSVQICAjb4U/gAAAAclBMVEX///8fpTofpTogpTogpTogpTrw+fLt+O/i8+be8uLb8d/T7djP7NXR7NbE58u548G34sCr3bWU1KGT05+FzpN+y413yIZ0x4RxxoJwxYFnwnliv3RWu2pUumhMt2FGtVxDs1k6sFE4r1Avq0cjpjwgpTpRW86VAAAAJnRSTlMAEXd3iO7//////////////////////////////////////////0c8mUAAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDUvMjkvMThc7W1eAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAHhJREFUGJVtzskSgkAMBFB2AUcR2QTcEPr/fxFmmFiW0znk8CrVac9jE1CNYqoAV8JaXTbq8K7/bBVcwRVcwRVcwbRTIDqcWrjaJ3cJ0VrdjDaHh2Sb2/F43XadPuWlTfjkJersLU2+ufNFnSdb0P/5thQv6R2CzQo9Oh+AlBF9bAAAAABJRU5ErkJggg==") no-repeat right bottom; +} + +.pay-btn-group .pay-cycle-btn:hover { + border: 1px solid #20a53a; + color: #20a53a; + z-index: 2; + cursor: pointer; +} + +.pay-btn-group .active, .pay-btn-group .active:hover { border: 2px solid #20a53a; color: #20a53a; z-index: 2; @@ -4589,22 +4658,22 @@ html .menu .menu_exit:hover { line-height: 22px; } .pay-cycle-btn em { - position: absolute; - right: -2px; - top: -9px; - width: 33px; - height: 19px; - background: #ff7300; - border-radius: 2px; - font-size: 10px; - line-height: 19px; - color: #fff; - transform: scale(.9, .8); - font-style: normal; + position: absolute; + right: -2px; + top: -9px; + width: 33px; + height: 19px; + background: #ff7300; + border-radius: 2px; + font-size: 10px; + line-height: 19px; + color: #fff; + transform: scale(.9, .8); + font-style: normal; } -.pay-btn-group .active em{ +.pay-btn-group .active em { right: -3px; - top: -10px; + top: -10px; } .paymethod{ margin: 0 auto; @@ -4614,12 +4683,12 @@ html .menu .menu_exit:hover { height:175px; } .paymethod .pay-wx canvas { - width: 160px; - height: 160px; - border: #ddd 1px solid; - margin: 6px auto; - padding: 5px; - border-bottom-color: #00c800; + width: 160px; + height: 160px; + border: #ddd 1px solid; + margin: 6px auto; + padding: 5px; + border-bottom-color: #00c800; } .lib-price-box{ padding-bottom: 5px; @@ -4726,21 +4795,21 @@ html .menu .menu_exit:hover { } /*重启*/ .rebt-con { - height: 106px; - padding: 15px; - width: 330px; + height: 106px; + padding: 15px; + width: 330px; } .rebt-li { - float: left; - line-height: 40px; - margin: 15px; - text-align: center; - width: 120px; + float: left; + line-height: 40px; + margin: 15px; + text-align: center; + width: 120px; } .rebt-li a { - display: block; - background: #eee; - border: #ddd 1px solid; + display: block; + background: #eee; + border: #ddd 1px solid; } .rebt-li a:hover{ background-color:#20a53a; @@ -4769,48 +4838,48 @@ html .menu .menu_exit:hover { } .bt-724 { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAQCAMAAACvHOZVAAAAA3NCSVQICAjb4U/gAAAAOVBMVEXeQRTxrJnkZUD76OLqiWzzuKfgTSP53NT////30MXvoIriWTHmcU/99PHslHvofV71xLbjXznpgGGbp65eAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFnRFWHRDcmVhdGlvbiBUaW1lADA3LzEwLzE5ht15uwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAACrSURBVCiRrZBLAsMgCETJRxwUTdP7H7b4SUnW7SxU4gMnQ/QP5TSlrVqDyGufV4usbVNMJSv2cVw6UIHexdoUUdjOBVHXAPBs1e9TB9AGctqybf3G6OKEVcmd7R2PCOJEwuaAFoi5RGQnbMRln3iB3dmcksmJ2m0OINpPMeWCTcR8y/hsTzpQ9B4BRmbAQWd6dzbyPYKqVzJEZ1vqo9N9VAk9BPMnU4/Uf9cHAxAHhkB15V0AAAAASUVORK5CYII="); - background-repeat: no-repeat; - padding-left: 35px; - margin-right: 20px; + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAQCAMAAACvHOZVAAAAA3NCSVQICAjb4U/gAAAAOVBMVEXeQRTxrJnkZUD76OLqiWzzuKfgTSP53NT////30MXvoIriWTHmcU/99PHslHvofV71xLbjXznpgGGbp65eAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFnRFWHRDcmVhdGlvbiBUaW1lADA3LzEwLzE5ht15uwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAACrSURBVCiRrZBLAsMgCETJRxwUTdP7H7b4SUnW7SxU4gMnQ/QP5TSlrVqDyGufV4usbVNMJSv2cVw6UIHexdoUUdjOBVHXAPBs1e9TB9AGctqybf3G6OKEVcmd7R2PCOJEwuaAFoi5RGQnbMRln3iB3dmcksmJ2m0OINpPMeWCTcR8y/hsTzpQ9B4BRmbAQWd6dzbyPYKqVzJEZ1vqo9N9VAk9BPMnU4/Uf9cHAxAHhkB15V0AAAAASUVORK5CYII="); + background-repeat: no-repeat; + padding-left: 35px; + margin-right: 20px; } /*系统图标*/ -.ico-linux{ +.ico-linux { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAATBAMAAACEi/vCAAAAA3NCSVQICAjb4U/gAAAAMFBMVEX///9mZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmambAcKAAAAEHRSTlMAESIzRFVmd4iZqrvM3e7/dpUBFQAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABZ0RVh0Q3JlYXRpb24gVGltZQAwOC8xNi8xOCL5ZhgAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAmUlEQVQImWNgYGCQ/GvAAAHz/1+CMBjPvvoOYTFVLXkNZZ2u+isAZjE/4vyvAGaxlgbnJ4BZHAoM3A/BLA8GBpafYNYiIL4PMpt1A5DYPwFIcDWArDkAJPgWAInzF4BE9jMGBvb/QM2SBfEGDFy7UhkY3Bk4CxisGdgCGMwYGI97vGKwSmAQC1G8e+aN2WagDuP0IEGbawIMAPDJKG706GgtAAAAAElFTkSuQmCC"); background-size: 12px; - background-repeat:no-repeat; - padding-left: 20px; + background-repeat: no-repeat; + padding-left: 20px; } -.ico-windows{ +.ico-windows { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAA3NCSVQICAjb4U/gAAAALVBMVEX///+ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZny8jBSAAAAD3RSTlMAESIzRFVmd4iZqrvM7v/Y8bBbAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFnRFWHRDcmVhdGlvbiBUaW1lADA4LzE2LzE4IvlmGAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAABGSURBVAiZY2AAAkbTqrMMDEJuHvfevXvHcPbdu+53IMY7Ihi7d+9O7QACBiB2BTOAol0wNbgY59696wQzGFQqw8GWwpwBAGURWs03JAEWAAAAAElFTkSuQmCC"); - background-repeat:no-repeat; + background-repeat: no-repeat; background-size: 12px; background-position: 0 2px; - padding-left: 20px; + padding-left: 20px; } -.ico-centos{ +.ico-centos { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAPCAMAAADjyg5GAAAAA3NCSVQICAjb4U/gAAAASFBMVEX///9mZmZmZmZiYmJaWlpYWFhmZmZiYmJgYGBmZmZgYGBmZmZiYmJmZmZiYmJmZmZiYmJmZmZmZmZmZmZmZmZmZmZmZmZmZmbwNFvuAAAAGHRSTlMAESIiIiIzMzNERFVVZmZ3d4iZqrvM3f93YyfbAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFnRFWHRDcmVhdGlvbiBUaW1lADA4LzE3LzE4mkUBfQAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAB6SURBVAiZVY5ZEsIwDEMdUpYQghIv9f1vWpNCp/hH1oz1LKI5if5G5ezuzLff7ibO7KK+H2LaBp1WDMLcYDNf3A31CXMvRCy995rSK0SExhjAkh/XEK5E2YOCN5r75RNGUJgtZJLXoMQjw/ot2LR3baJHSbN8Ll3LrhtQxAfrvVHLpQAAAABJRU5ErkJggg=="); background-size: 12px; - background-repeat:no-repeat; - padding-left: 20px; + background-repeat: no-repeat; + padding-left: 20px; } -.ico-ubuntu{ +.ico-ubuntu { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAAA3NCSVQICAjb4U/gAAAAOVBMVEX///9mZmZhYWFmZmZiYmJmZmZiYmJmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZVQ1Z+AAAAE3RSTlMAEREiIjMzRFVmd4iZqrvM3e7/67N/KAAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABZ0RVh0Q3JlYXRpb24gVGltZQAwOC8xNy8xOJpFAX0AAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAbElEQVQImU2OWQ6AMAhEcW3Z2jL3P6xWjHE+CMPwCESfDOXtFmutAp6OtwC0jUwZVlWHaaaBniyYVq6B/Rn3G3aEjDxRuPwtN6NFzkB97IAlE3ddHTimPUKkd3TlhLb5huvLzAWWE2j0k0elCynoBUe/zXGJAAAAAElFTkSuQmCC"); background-size: 12px; - background-repeat:no-repeat; - padding-left: 20px; + background-repeat: no-repeat; + padding-left: 20px; } -.ico-debian{ +.ico-debian { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAA3NCSVQICAjb4U/gAAAAPFBMVEX///9paWlmZmZgYGBdXV1mZmZgYGBmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYdQ+x6AAAAFHRSTlMAERERESIiM0RVZneImaq7zN3u/ynguF0AAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDgvMTcvMTiaRQF9AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAHJJREFUCJlVjlESwjAIRLFFIRhCaO5/VyHijN2vt+zCABBq87IhT/hK1+zdfOl2Y71r2rPp1UKfAOdFgaTtAWIRWgacMUUquSi7TR6U2LflFuW0nOVjYB7JQBqwHklOe49f9dOAf+HvjRIZ3L3ePTIWfQAgiAODqr7Z+QAAAABJRU5ErkJggg=="); background-size: 12px; - background-repeat:no-repeat; - padding-left: 20px; + background-repeat: no-repeat; + padding-left: 20px; } -.ico-fedora{ +.ico-fedora { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAAA3NCSVQICAjb4U/gAAAAPFBMVEX///9VVVVLS0tmZmZaWlpmZmZiYmJmZmZiYmJmZmZiYmJiYmJmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYd2jWRAAAAFHRSTlMAEREiIjMzRERVVWaImaq7zN3u/2KENScAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDgvMTcvMTiaRQF9AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAGJJREFUCJlFj1kSwCAIQ9N9Lxjuf9faUKf5IPNQiAJAZ4yg9ZCWkMj5pxWwGCqK5skLg8AphMv2dIJC0106nNmu5VLA1bBmfqclF7fZO9HTYqzO4tMuOt5XGLA1enMzQ194ADH/CrSqGfFbAAAAAElFTkSuQmCC"); background-size: 12px; - background-repeat:no-repeat; - padding-left: 20px; + background-repeat: no-repeat; + padding-left: 20px; } .index-pos-box{ height: 52px; @@ -4829,143 +4898,143 @@ html .menu .menu_exit:hover { margin-right: 3px; } /*微信二维码*/ - .boxConter { - height: 458px; - position: relative; - overflow: auto; +.boxConter { + height: 458px; + position: relative; + overflow: auto; } .iconCode { - padding: 50px 60px; + padding: 50px 60px; } .box-conter { - width: 100%; + width: 100%; } #QRcode { - margin-bottom: 25px; - text-align: center; + margin-bottom: 25px; + text-align: center; } .iconCode #QRcode, .iconCode .codeTip { - width: 100%; - text-align: center; - font-size: 17px; + width: 100%; + text-align: center; + font-size: 17px; } .iconCode .weChatSamll img { - width: 100%; + width: 100%; } .iconCode .weChatSamll { - display: none; - width: 200px; - height: 200px; - position: absolute; - border: 1px solid #ececec; - border-radius: 5px; - bottom: 150px; - right: 50px; - padding: 20px; - background-color: #fff; + display: none; + width: 200px; + height: 200px; + position: absolute; + border: 1px solid #ececec; + border-radius: 5px; + bottom: 150px; + right: 50px; + padding: 20px; + background-color: #fff; } .iconCode .weChatSamll:after { - content: ''; - width: 15px; - height: 15px; - background: #ffffff; - border-bottom: 1px solid #ececec; - border-right: 1px solid #ececec; - transform: rotate(45deg); - position: absolute; - border-radius: 4px; - left: 90px; - bottom: -8px; + content: ''; + width: 15px; + height: 15px; + background: #ffffff; + border-bottom: 1px solid #ececec; + border-right: 1px solid #ececec; + transform: rotate(45deg); + position: absolute; + border-radius: 4px; + left: 90px; + bottom: -8px; } .iconCode .weChat { - margin-left: 15px; + margin-left: 15px; } .iconCode .weChat:hover .weChatSamll { - display: block; + display: block; } .iconCode .QRcode { - margin-bottom: 15px; - text-align: center; + margin-bottom: 15px; + text-align: center; } .codeTip ul li { - margin-bottom: 10px; + margin-bottom: 10px; } .personalDetails .head_img { - width: 50px; - height: 50px; - float: left; - margin-right: 30px; + width: 50px; + height: 50px; + float: left; + margin-right: 30px; } .personalDetails .head_img img { - height: 100%; - border-radius: 50%; + height: 100%; + border-radius: 50%; } .personalDetails .nick_name { - height: 50px; - line-height: 50px; - width: 148px; - float: left; - font-size: 15px; - color: #808080; + height: 50px; + line-height: 50px; + width: 148px; + float: left; + font-size: 15px; + color: #808080; } .personalDetails .userList { - height: 100%; + height: 100%; } .personalDetails .userList .addweChat { - height: 50px; - text-align: center; - padding-top: 20px; - color: #20a53a; - font-size: 16px; + 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; + 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; + height: 50px; + width: 60px; + float: right; + line-height: 50px; + text-align: center; } .libLogin .line{ position:relative; } .libLogin .tips { - position: absolute; - top: 13px; - right: 28px; - background: #ff4949; - color: #fff; - padding: 4px 6px; - border-radius: 5px; + position: absolute; + top: 13px; + right: 28px; + background: #ff4949; + color: #fff; + padding: 4px 6px; + border-radius: 5px; } .change-id{ cursor:pointer; @@ -5049,9 +5118,9 @@ select[disabled]{ .mem-action .occupy{ font-size: 14px; } -.mem-action .occupy.line{ +.mem-action .occupy.line { line-height: 22px; - padding-top: 22px; + padding-top: 22px; } .change-default button{ @@ -5062,26 +5131,26 @@ select[disabled]{ margin-left:0; } -.bt-logs textarea{ +.bt-logs textarea { white-space: pre; - margin: 0px; - width: 500px; - height: 520px; - background-color: #333; - color: #fff; - padding: 0 5px; + margin: 0px; + width: 500px; + height: 520px; + background-color: #333; + color: #fff; + padding: 0 5px; } -.dir-rewrite-man-con{ - margin-left: 15px; +.dir-rewrite-man-con { + margin-left: 15px; } -.domain-ul-list{ +.domain-ul-list { padding: 5px 10px; - max-height: 180px; - overflow: auto; - width: 260px; - border: #ccc 1px solid; - border-radius: 3px; + max-height: 180px; + overflow: auto; + width: 260px; + border: #ccc 1px solid; + border-radius: 3px; } .domain-ul-list li{ line-height:26px; @@ -5094,27 +5163,27 @@ select[disabled]{ width:2.4em;height:1.4em;margin-bottom: 0 } -.db_list{ +.db_list { padding: 0px 0 10px 0; - font-size: 13px; - line-height: 35px; - height: 45px; + font-size: 13px; + line-height: 35px; + height: 45px; } -.db_list a{ +.db_list a { margin-right: 15px; color: #555; - font-weight: 600; + font-weight: 600; } -.db_list span:nth-child(1){ +.db_list span:nth-child(1) { display: block; - float: left; - padding-left: 15px; - background: #ececec; - border-radius: 3px; + float: left; + padding-left: 15px; + background: #ececec; + border-radius: 3px; } #db_tools button { - margin-right: 8px; + margin-right: 8px; } #db_tools button:last-child{ margin-right: 0; @@ -5143,14 +5212,14 @@ select[disabled]{ .tab-db-status .info-r{ margin-bottom: 0px; } -.tab-db-status .line:nth-child(1) .ml0{ +.tab-db-status .line:nth-child(1) .ml0 { margin-bottom: 10px; - padding-bottom: 8px; - border-bottom: 1px solid #ccc; + padding-bottom: 8px; + border-bottom: 1px solid #ccc; } -.tab-db-status .bt_mysql_restart{ +.tab-db-status .bt_mysql_restart { margin-left: 75px; - margin-top: 30px; + margin-top: 30px; } .bt_table table td span.fixed, #database_fix table td span{ @@ -5194,11 +5263,11 @@ select[disabled]{ #proxy_list tr td:nth-child(3) >span{ max-width: 80px; } -#proxy_list tr th:last-child,#proxy_list tr td:last-child >span{ - text-align: right; +#proxy_list tr th:last-child,#proxy_list tr td:last-child >span { + text-align: right; } -#softList tr td:nth-child(3) >span{ - white-space: inherit; +#softList tr td:nth-child(3) >span { + white-space: inherit; min-width: 151px; } #proxy_config_con .line .info-r, @@ -5270,11 +5339,11 @@ select[disabled]{ margin-left: 170px; } -.search_box{ +.search_box { position: absolute; - right: 65px; - top: 16px; - height: 16px; + right: 65px; + top: 16px; + height: 16px; } .search_box input{ vertical-align: sub; @@ -5345,27 +5414,27 @@ select[disabled]{ white-space: nowrap; } .codebg { - background-color: #333; - max-height: 98%; + background-color: #333; + max-height: 98%; } .codebg code { - background: none; - color: #fff; - padding: 0; - display: block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + background: none; + color: #fff; + padding: 0; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .waiting-down-list li { margin: 10px 0; } -.waiting-down-list li .down-filse-name .fname{ +.waiting-down-list li .down-filse-name .fname { color: #333; - display:inline-block; + display: inline-block; width: 220px; overflow: hidden; - text-overflow:ellipsis; + text-overflow: ellipsis; white-space: nowrap; } .waiting-down-list li .down-progress{ @@ -5419,135 +5488,135 @@ select[disabled]{ background-color:#20a53a; color:#fff; } -.oldTableShadow{ +.oldTableShadow { width: 100%; - height: 8px; - margin-top: -8px; - position: relative; - background: -webkit-linear-gradient(top,rgba(255, 255, 255, 0),rgba(220, 220, 220, .8)); + height: 8px; + margin-top: -8px; + position: relative; + background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0), rgba(220, 220, 220, .8)); } -.newTableShadow{ +.newTableShadow { width: auto; - height: 8px; - position: absolute; - left: 15px; - right: 15px; - background: -webkit-linear-gradient(top,rgba(220, 220, 220, .8),rgba(255, 255, 255, 0)); + height: 8px; + position: absolute; + left: 15px; + right: 15px; + background: -webkit-linear-gradient(top, rgba(220, 220, 220, .8), rgba(255, 255, 255, 0)); } .disable { - background-color: #ededed; + background-color: #ededed; } .onekeyCode .table > tbody > tr > td { - line-height: 1.7; + line-height: 1.7; } .oneKeysearchBar { - position: absolute; - top: 10px; - right: 15px; - z-index: 20; + position: absolute; + top: 10px; + right: 15px; + z-index: 20; } .success-msg .suc-con { - float: right; - width: 280px; + float: right; + width: 280px; } .bt-progress { - background-color: #e2e2e2; - border-radius: 8px; - height: 16px; - line-height: 16px; - position: relative; - margin: 5px 0; + background-color: #e2e2e2; + border-radius: 8px; + height: 16px; + line-height: 16px; + position: relative; + margin: 5px 0; } .bt-progress-bar { - background-color: #5ab76c; - border-radius: 8px; - height: 16px; - max-width: 100%; - position: absolute; - text-align: right; - transition: all 0.3s ease 0s; - width: 0; + background-color: #5ab76c; + border-radius: 8px; + height: 16px; + max-width: 100%; + position: absolute; + text-align: right; + transition: all 0.3s ease 0s; + width: 0; } .bt-progress-text { - font-size: 12px; - color: #fff; - padding: 0 10px; - position: static; + font-size: 12px; + color: #fff; + padding: 0 10px; + position: static; } .onekey-menu-sub { - background-color: #fff; - border-bottom: 1px solid #ddd; - height: 50px; - line-height: 50px; + background-color: #fff; + border-bottom: 1px solid #ddd; + height: 50px; + line-height: 50px; } .onekey-menu-sub span { - height: 50px; - padding: 0 15px; - cursor: pointer; - display: inline-block; - font-size: 14px; + height: 50px; + padding: 0 15px; + cursor: pointer; + display: inline-block; + font-size: 14px; } .onekey-menu-sub .on { - border-bottom: 2px solid #20a53a; - color: #20a53a; - font-weight: bold; + border-bottom: 2px solid #20a53a; + color: #20a53a; + font-weight: bold; } -.ssl_cert_from .layui-layer-ico{ +.ssl_cert_from .layui-layer-ico { width: 30px; - height: 30px; - display: inline-block; - position: absolute; - left: 50px; + height: 30px; + display: inline-block; + position: absolute; + left: 50px; } -.ssl_cert_from h3{ +.ssl_cert_from h3 { font-weight: bolder; font-size: 18px; margin-left: 75px; display: inline-block; height: 30px; - line-height: 30px; + line-height: 30px; } -.ssl_cert_from ul{ - border: 1px solid #ececec; - border-radius: 10px; - margin: 0 auto; - margin-top: 20px; - margin-bottom: 20px; - background: #f7f7f7; - width: 80%; - padding: 15px; - list-style-type: inherit; +.ssl_cert_from ul { + border: 1px solid #ececec; + border-radius: 10px; + margin: 0 auto; + margin-top: 20px; + margin-bottom: 20px; + background: #f7f7f7; + width: 80%; + padding: 15px; + list-style-type: inherit; } .ssl_cert_from ul li:nth-child(0){ margin-top: 12px; color:red; } -.ssl_cert_from ul li{ +.ssl_cert_from ul li { margin-left: 20px; - height: 25px; - line-height: 25px; + height: 25px; + line-height: 25px; } -.ssl_cert_from>.line{ +.ssl_cert_from>.line { padding-top: 15px; - border-top: 1px solid #ececec; - width: 81%; - margin: 0 auto; - margin-top: 15px; - padding-bottom: 15px; - border-bottom: 1px solid #ececec; + border-top: 1px solid #ececec; + width: 81%; + margin: 0 auto; + margin-top: 15px; + padding-bottom: 15px; + border-bottom: 1px solid #ececec; } .ssl_cert_from>.line .tname{ width:105px; @@ -5558,36 +5627,32 @@ select[disabled]{ height:30px; line-height:30px; } -.user_set_info .tit{ +.user_set_info .tit { width: 160px; - display: inline-block; + display: inline-block; text-align: right; padding-right: 20px; } -.user_set_info .btswitch-p{ +.user_set_info .btswitch-p { width: 165px; display: inline-block; text-align: left; margin-left: 0px; } -.ssl_cert_from label{ - font-weight: 400; - margin: 3px 5px 0px; - vertical-align: top; +.ssl_cert_from label { + font-weight: 400; + margin: 3px 5px 0px; + vertical-align: top; } .ssl_cert_from .details{ - padding-top:15px; + padding-top:10px; width:80%; margin:0 auto; } -.ssl_cert_from .details input { - margin-top: 1px; -} -.ssl_cert_from .details label { - margin-top: 0!important; -} -.ssl_cert_from .details a{ +.ssl_cert_from .details a { float: right; + position: relative; + top: -1px; } .ssl_cert_from>.line .line { @@ -5612,11 +5677,11 @@ select[disabled]{ } .ace_overall { width: 100%; - display: inline-block; - position: absolute; - bottom: 0; - background: #777; - transition: top 500ms; + display: inline-block; + position: absolute; + bottom: 0; + background: #777; + transition: top 500ms; } .ace_overall.active .ace_editor_main{ margin-left: 0 !important; @@ -5651,23 +5716,23 @@ select[disabled]{ .ace_catalogue_title { height: 45px; line-height: 45px; - padding:0 15px; + padding: 0 15px; color: #fff; background: #383838; position: absolute; left: 0; right: 0; white-space: nowrap; - overflow: hidden; + overflow: hidden; text-overflow: ellipsis; cursor: pointer; } -.ace_catalogue_list{ +.ace_catalogue_list { height: auto; - position: absolute; - top: 70px; - left: 0; - right: 1px; + position: absolute; + top: 70px; + left: 0; + right: 1px; bottom: 0; background: #222; padding: 5px; @@ -5690,14 +5755,14 @@ select[disabled]{ background: #333; border-radius: 0; } -.ace_catalogue_drag_icon{ +.ace_catalogue_drag_icon { height: 100%; - width: 1px; - position: absolute; + width: 1px; + position: absolute; left: 265px; bottom: 0; top: 0; - cursor: col-resize; + cursor: col-resize; } .ace_catalogue_drag_icon .drag_icon_conter{ height: 100%; @@ -5706,36 +5771,36 @@ select[disabled]{ .ace_catalogue_drag_icon .fold_icon_conter.active::after{ content: "\e258"; } -.ace_catalogue_drag_icon .fold_icon_conter::after{ +.ace_catalogue_drag_icon .fold_icon_conter::after { color: #ececec; content: "\e257"; - position: absolute; - top: 50%; - margin-top: -6.5px; - left: 0; - z-index: 999; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: 400; - line-height: 1; + position: absolute; + top: 50%; + margin-top: -6.5px; + left: 0; + z-index: 999; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: 400; + line-height: 1; -webkit-font-smoothing: antialiased; cursor: pointer; } -.ace_catalogue_drag_icon .fold_icon_conter{ +.ace_catalogue_drag_icon .fold_icon_conter { display: inline-block; - content: ''; - height: 50px; - width: 14px; - margin-top: -50px; - position: absolute; - z-index: 999; - top: 48%; - left: -1px; - background: #222; - border-top-right-radius: 15px; - border-bottom-right-radius: 15px; - border: 1px solid #525252; + content: ''; + height: 50px; + width: 14px; + margin-top: -50px; + position: absolute; + z-index: 999; + top: 48%; + left: -1px; + background: #222; + border-top-right-radius: 15px; + border-bottom-right-radius: 15px; + border: 1px solid #525252; border-left: none; cursor: pointer; } @@ -5777,68 +5842,68 @@ select[disabled]{ .ace_editors.active { display: block; } -.ace_dir_tools{ +.ace_dir_tools { height: 30px; - top: 40px; - position: absolute; - left: 0; - right: 0; - z-index: 999; - background: #444; + top: 40px; + position: absolute; + left: 0; + right: 0; + z-index: 999; + background: #444; } -.folder_down_up{ +.folder_down_up { padding: 5px 0; - width: 150px; - border: 1px solid #ececec; - position: fixed; - z-index: 9999; - background: #fff; - border-radius: 2px; - position: absolute; - left:0; - display: none; + width: 150px; + border: 1px solid #ececec; + position: fixed; + z-index: 9999; + background: #fff; + border-radius: 2px; + position: absolute; + left: 0; + display: none; } -.folder_down_up i.folder-icon:before{ +.folder_down_up i.folder-icon:before { color: #d6ab34; - content: '\f016'; - font-family: octicons; - display: inline-block; - font-size: 15px; -} -.folder_down_up i{ - font-style: normal; - position: absolute; - left: 10px; -} -.folder_down_up li:hover{ - background: #efefef; - cursor: pointer; -} -.folder_down_up li{ - list-style-type: none; - margin: 0; - padding: 0; - width: 150px; - height: 30px; - font-size: 13px; - line-height: 30px; - padding-left: 35px; - box-sizing: border-box; - position: relative; - color: #555; - left: -1px; - font-style: normal; -} -.ace_dir_tools>div{ + content: '\f016'; + font-family: octicons; display: inline-block; - height: 30px; - line-height: 30px; - box-sizing: border-box; - color: #ccc; - padding: 0 5px; - cursor: pointer; - position: relative; + font-size: 15px; +} +.folder_down_up i { + font-style: normal; + position: absolute; + left: 10px; +} +.folder_down_up li:hover { + background: #efefef; + cursor: pointer; +} +.folder_down_up li { + list-style-type: none; + margin: 0; + padding: 0; + width: 150px; + height: 30px; + font-size: 13px; + line-height: 30px; + padding-left: 35px; + box-sizing: border-box; + position: relative; + color: #555; + left: -1px; + font-style: normal; +} +.ace_dir_tools>div { + display: inline-block; + height: 30px; + line-height: 30px; + box-sizing: border-box; + color: #ccc; + padding: 0 5px; + cursor: pointer; + position: relative; } .ace_dir_tools>span{ left: 0; @@ -5858,11 +5923,11 @@ select[disabled]{ .ace_dir_tools>div i{ margin: 0 3px; } -.ace_dir_tools .search_input_view{ +.ace_dir_tools .search_input_view { width: 100%; - padding: 0 15px; - height: auto; - padding-top: 10px; + padding: 0 15px; + height: auto; + padding-top: 10px; } .ace_dir_tools .search_input_view:hover, .ace_dir_tools .search_input_title:hover{ @@ -5889,35 +5954,35 @@ select[disabled]{ padding-left: 15px; cursor: default; } -.ace_dir_tools .search_input_view .search_boxs{ +.ace_dir_tools .search_input_view .search_boxs { margin-top: 5px; - height: 20px; - line-height: 20px; -} -.ace_dir_tools .search_input_view .search_boxs{ - margin-top: 5px; - height: 20px; - line-height: 20px; -} -.ace_dir_tools .search_input_view .search_boxs input{ - display: inline-block; - width: 20px; - height: 20px; - border-radius: 0px; -} -.ace_dir_tools .search_input_view .search_boxs label{ height: 20px; - margin: 0; + line-height: 20px; +} +.ace_dir_tools .search_input_view .search_boxs { + margin-top: 5px; + height: 20px; + line-height: 20px; +} +.ace_dir_tools .search_input_view .search_boxs input { + display: inline-block; + width: 20px; + height: 20px; + border-radius: 0px; +} +.ace_dir_tools .search_input_view .search_boxs label { + height: 20px; + margin: 0; } .ace_dir_tools .search_input_view .search_boxs span{ vertical-align: super; margin-right: 5px; } -#search_input_val{ +#search_input_val { outline: none; - color: #333; - padding-right: 45px; - width: 100%; + color: #333; + padding-right: 45px; + width: 100%; } .ace_header { position: relative; @@ -5936,9 +6001,9 @@ select[disabled]{ border-right: 1px solid #4c4c4c; cursor: pointer; } -.ace_header>span i{ +.ace_header>span i { margin-right: 5px; - vertical-align: text-top; + vertical-align: text-top; } .ace_header>span .fa { margin-right: 5px; @@ -5946,16 +6011,16 @@ select[disabled]{ .ace_header>span:hover { background: #2f2f2f; } -.ace_header .pull-down{ +.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; + right: 0; + height: 35px; + line-height: 35px; + padding: 0 14px; + font-size: 15px; + color: #fff; background: #292929; cursor: pointer; } @@ -5989,8 +6054,8 @@ select[disabled]{ cursor: pointer; border-right: 1px solid #191919; white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + overflow: hidden; + text-overflow: ellipsis; } .ace_conter_menu .item:hover { background: #313131; @@ -6206,11 +6271,11 @@ select[disabled]{ } .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 + -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; @@ -6219,12 +6284,12 @@ select[disabled]{ padding: 25px; overflow: hidden; } -.helps_conter .helps_item{ +.helps_conter .helps_item { margin-bottom: 10px; - display: inline-block; - border-left: 3px solid #20a53a; - padding-left: 5px; - font-size: 15px; + display: inline-block; + border-left: 3px solid #20a53a; + padding-left: 5px; + font-size: 15px; } .helps_conter .helps_box{ margin-bottom: 20px; @@ -6241,8 +6306,8 @@ select[disabled]{ padding:15px; } -.cursor-row,.cursor-line{ - margin:5px; +.cursor-row,.cursor-line { + margin: 5px; } .set_font_size, .set_jump_line { margin-top: 20px; @@ -6251,12 +6316,12 @@ select[disabled]{ .set_font_size input, .set_jump_line input{ width: 250px; } -.set_font_size .btn-save, .set_jump_line .btn-save{ - margin-left: 15px; - height: 35px; - width: 80px; - border: none; - background: #20a53a; +.set_font_size .btn-save, .set_jump_line .btn-save { + margin-left: 15px; + height: 35px; + width: 80px; + border: none; + background: #20a53a; color: #fff; outline: none; transition: all 500ms; @@ -6276,96 +6341,96 @@ select[disabled]{ } .ssl-file-error p span { - font-weight: 600; - margin-right: 10px; + font-weight: 600; + margin-right: 10px; } .dropdown-menu-li { - position: absolute; - top: 0; - left: 100%; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - font-size: 14px; - text-align: left; - list-style: none; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0,0,0,.15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0,0,0,.175); - box-shadow: 0 6px 12px rgba(0,0,0,.175); + position: absolute; + top: 0; + left: 100%; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); } .dropdown-menu-li.pull-right { - right: 0; - left: auto + right: 0; + left: auto } .dropdown-menu-li > li > div > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: 400; - line-height: 1.42857143; - color: #333; - white-space: nowrap; - display: inline-block; - padding: 0 15px 0 33px; + display: block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #333; + white-space: nowrap; + display: inline-block; + padding: 0 15px 0 33px; } .dropdown-menu-li > li:hover, .dropdown-menu > li:hover { - color: #262626; - text-decoration: none; - background-color: #f5f5f5 + color: #262626; + text-decoration: none; + background-color: #f5f5f5 } .file-types { - position: relative; - line-height: 30px; - height: 30px; + position: relative; + line-height: 30px; + height: 30px; } .file-types .ico-folder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMpJREFUeNpi/P//P8NAAiaGAQYD7gAWdIFPpxIoMS8ZiOcQqfY5SD0LFT1jAsRTuaU0GZjZuAgq/vPto+S3V3fmUssBokC8llNEjp2ZFWjk/1+Eg56TE0RJsgCD3BPImAvikGs7IzMLAxuvKAMrNz9ZaWAul5iKJAsXPwUBAMzK//9AaDIcIAkODiKCbbQcGHXAqANGHTDqgFEHjDpg1AGjDqCVA57/+f6Z7hZD7XwBahOmfHv1ANQqlqCzG54CcRrjaOd0oB0AEGAAscwsxMSUtNsAAAAASUVORK5CYII="); + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMpJREFUeNpi/P//P8NAAiaGAQYD7gAWdIFPpxIoMS8ZiOcQqfY5SD0LFT1jAsRTuaU0GZjZuAgq/vPto+S3V3fmUssBokC8llNEjp2ZFWjk/1+Eg56TE0RJsgCD3BPImAvikGs7IzMLAxuvKAMrNz9ZaWAul5iKJAsXPwUBAMzK//9AaDIcIAkODiKCbbQcGHXAqANGHTDqgFEHjDpg1AGjDqCVA57/+f6Z7hZD7XwBahOmfHv1ANQqlqCzG54CcRrjaOd0oB0AEGAAscwsxMSUtNsAAAAASUVORK5CYII="); } .file-types .ico-file { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAOVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gJMDAMMRh0w6oBRB4w6YNQBow4YdcCoA0YdMOgc8Pzbt280swxq9gt8reKU58+fgzqnEjRyw1MgTkMWYBzo7jlAgAEAzk5sMbucHicAAAAASUVORK5CYII="); + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAOVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gJMDAMMRh0w6oBRB4w6YNQBow4YdcCoA0YdMOgc8Pzbt280swxq9gt8reKU58+fgzqnEjRyw1MgTkMWYBzo7jlAgAEAzk5sMbucHicAAAAASUVORK5CYII="); } .file-types .ico { - background-position: center center; - background-repeat: no-repeat; - background-size: 15px; - display: inline-block; - height: 15px; - margin-right: 10px; - margin-left: 5px; - width: 16px; - vertical-align: text-top; + background-position: center center; + background-repeat: no-repeat; + background-size: 15px; + display: inline-block; + height: 15px; + margin-right: 10px; + margin-left: 5px; + width: 16px; + vertical-align: text-top; } .file-type-li { - position: relative; + position: relative; } .file-type-li .ico { - position: absolute; + position: absolute; } .file-type-span { - position: absolute; - right: 6px; - top: 7px; - font-size: 11px; - transform: scale(.7); + position: absolute; + right: 6px; + top: 7px; + font-size: 11px; + transform: scale(.7); } @@ -6387,17 +6452,17 @@ select[disabled]{ line-height: 100px; color: #666666; } -.comment_num_tips{ +.comment_num_tips { list-style-type: none; - width: 145px; - height: 100px; - display: inline-block; - vertical-align: top; - padding-left: 10px; - padding: 20px 0 20px 15px; - box-sizing: border-box; - margin: 0; - line-height: 19px; + width: 145px; + height: 100px; + display: inline-block; + vertical-align: top; + padding-left: 10px; + padding: 20px 0 20px 15px; + box-sizing: border-box; + margin: 0; + line-height: 19px; } .comment_num_tips li{ color: #888; @@ -6545,26 +6610,26 @@ select[disabled]{ font-size: 13px; float: right; } -.comment_box_title .nice_name{ +.comment_box_title .nice_name { font-weight: 500; - color: #777; - font-size: 13px; - display: inline-block; - width: 90px; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - vertical-align: middle; + color: #777; + font-size: 13px; + display: inline-block; + width: 90px; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + vertical-align: middle; } .comment_box_content{ color: #777; font-size: 13px; height: 36px; } -.edit_view.active{ +.edit_view.active { cursor: no-drop; - background: #ddd; - border: 1px solid #ccc; + background: #ddd; + border: 1px solid #ccc; } .edit_view.active:hover{ background: #ccc; @@ -6586,7 +6651,7 @@ select[disabled]{ font-size: 15px; position: relative; left: 50%; - cursor: pointer; + cursor: pointer; transition: 500ms all; margin: 15px 0 8px -100px; } @@ -6600,18 +6665,18 @@ select[disabled]{ padding: 10px 0; display: none; } -.comment_box.get_next_page{ +.comment_box.get_next_page { display: block; - width: auto; - padding: 12px; - text-align: center; - font-size: 14px; - color: #888; + width: auto; + padding: 12px; + text-align: center; + font-size: 14px; + color: #888; line-height: 14px; box-sizing: border-box; cursor: pointer; transition: all 500ms; - margin:1%; + margin: 1%; float: none; clear: both; } @@ -6629,13 +6694,13 @@ select[disabled]{ .add_score_view{ padding: 30px 30px 0 30px; } -.score_icon_group{ +.score_icon_group { text-align: center; - height: 30px; - line-height: 30px; + height: 30px; + line-height: 30px; font-size: 32px; margin-bottom: 2px; - color: #cecece; + color: #cecece; } .score_icon_group_tips{ text-align: center; @@ -6652,10 +6717,10 @@ select[disabled]{ .score_icon_group span.active{ color: #F6BA2A; } -.score_input{ - width: auto; - display: block; - width: 100%; +.score_input { + width: auto; + display: block; + width: 100%; height: 130px; padding: 7px 5px 5px 8px; margin-top: 10px; @@ -6706,11 +6771,11 @@ select[disabled]{ display: none; position: relative; } -.cd-accordion-menu .has-children .file_fold.active{ - color: rgb(204, 167, 0); +.cd-accordion-menu .has-children .file_fold.active { + color: rgb(204, 167, 0); } -.cd-accordion-menu .has-children .file_fold.bg{ - background: #2f2f2f; +.cd-accordion-menu .has-children .file_fold.bg { + background: #2f2f2f; } .cd-accordion-menu .has-children .file_fold.bg:hover{ background: #2f2f2f; @@ -6718,7 +6783,7 @@ select[disabled]{ .cd-accordion-menu .has-children .file_fold:hover{ background-color: #272727; } -.cd-accordion-menu .has-children .file_fold{ +.cd-accordion-menu .has-children .file_fold { margin-bottom: 0; padding-left: 10px; width: 100%; @@ -6728,49 +6793,49 @@ select[disabled]{ position: relative; height: 28px; white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + overflow: hidden; + text-overflow: ellipsis; } -.cd-accordion-menu .has-children .file_fold>.glyphicon{ +.cd-accordion-menu .has-children .file_fold>.glyphicon { position: absolute; - left: 10px; + left: 10px; top: 7px; font-size: 12px; } -.cd-accordion-menu .has-children .file_title{ +.cd-accordion-menu .has-children .file_title { position: relative; left: 20px; font-weight: 500; height: 27px; display: inline-block; - width: 85%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + width: 85%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } -.cd-accordion-menu .has-children .file_input{ +.cd-accordion-menu .has-children .file_input { position: relative; left: 20px; font-weight: 500; height: 28px; display: inline-block; - width: 100%; + width: 100%; } .cd-accordion-menu .has-children .file_input i, -.cd-accordion-menu .has-children .file_title i{ +.cd-accordion-menu .has-children .file_title i { font-style: normal; font-size: 16px; - margin-right: 8px; + margin-right: 8px; vertical-align: bottom; width: 15px; height: 28px; - display: inline-block; + display: inline-block; } -.cd-accordion-menu .has-children .folder-icon{ +.cd-accordion-menu .has-children .folder-icon { font-style: normal; font-size: 14px; margin-right: 7px; - vertical-align: bottom; + vertical-align: bottom; } .ace_catalogue_menu .folder-icon:before, .cd-accordion-menu .has-children .folder-icon:before{ @@ -6794,11 +6859,11 @@ select[disabled]{ cursor: pointer; color:#cccccc; } -.has_children_separator{ +.has_children_separator { position: absolute; - top: 29px; - bottom: 0; - width: 1px; + top: 29px; + bottom: 0; + width: 1px; background: #333; left: 15px; } @@ -6835,65 +6900,65 @@ select[disabled]{ .children_8 .has_children_separator{left: 120px!important;} /* 文字省略 */ -.size_ellipsis{ +.size_ellipsis { display: inline-block; white-space: nowrap; - overflow: hidden; + overflow: hidden; text-overflow: ellipsis; } -.ace_conter_tips{ +.ace_conter_tips { height: 50px; - background: #d29232; - z-index: 99999; - position: absolute; - width: 100%; + background: #d29232; + z-index: 99999; + position: absolute; + width: 100%; display: none; bottom: -1px; } -.ace_conter_tips .tips{ +.ace_conter_tips .tips { display: inline-block; - padding-left: 15px; - color: #fff; - font-size: 14px; - font-weight: 600; - height: 100%; + padding-left: 15px; + color: #fff; + font-size: 14px; + font-weight: 600; + height: 100%; line-height: 50px; vertical-align: top; transition: all 500ms; } -.ace_catalogue_menu{ +.ace_catalogue_menu { padding: 5px 0; - width: 150px; - border: 1px solid #ececec; - position: fixed; - z-index: 9999; - background: #fff; - border-radius: 2px; - display: none; + width: 150px; + border: 1px solid #ececec; + position: fixed; + z-index: 9999; + background: #fff; + border-radius: 2px; + display: none; } -.ace_catalogue_menu li{ +.ace_catalogue_menu li { list-style-type: none; - margin: 0; - padding: 0; - width: 150px; - height: 30px; - font-size: 13px; - line-height: 30px; - padding-left: 35px; - box-sizing: border-box; - position: relative; - color: #555; - left: -1px; + margin: 0; + padding: 0; + width: 150px; + height: 30px; + font-size: 13px; + line-height: 30px; + padding-left: 35px; + box-sizing: border-box; + position: relative; + color: #555; + left: -1px; } .ace_catalogue_menu li:hover{ background: #efefef; cursor: pointer; } -.ace_catalogue_menu li i{ +.ace_catalogue_menu li i { font-style: normal; - position: absolute; - left: 10px; + position: absolute; + left: 10px; } .ace_catalogue_menu li i:before{ font-size: 17px; @@ -6905,44 +6970,44 @@ select[disabled]{ padding-left:5px; border: 1px solid #528bff; } -.file_input .glyphicon-ok{ +.file_input .glyphicon-ok { position: absolute; - right: 46px; - left: auto; - display: inline-block; - color: #20a53a; - width: 20px; - height: 26px; - line-height: 26px; - text-align: center; + right: 46px; + left: auto; + display: inline-block; + color: #20a53a; + width: 20px; + height: 26px; + line-height: 26px; + text-align: center; } -.file_input .glyphicon-remove{ +.file_input .glyphicon-remove { position: absolute; - right: 22px; - left: auto; - display: inline-block; - color: #f34a4a; - width: 20px; - height: 26px; - line-height: 27px; - text-align: center; + right: 22px; + left: auto; + display: inline-block; + color: #f34a4a; + width: 20px; + height: 26px; + line-height: 27px; + text-align: center; } -.ace_catalogue_menu .rename-icon{ - background-position: 0 -64px; -}.ace_catalogue_menu .del-icon{ - background-position: 0 -80px; -} -.ace_catalogue_menu .img-icon{ +.ace_catalogue_menu .rename-icon { + background-position: 0 -64px; +}.ace_catalogue_menu .del-icon { + background-position: 0 -80px; + } +.ace_catalogue_menu .img-icon { display: inline-block; - background-image: url('../img/file_menu_icon.png'); - width: 16px; - height: 16px; - position: absolute; - top: 50%; - margin-top: -8.5px; - font-style: normal; - background-size: auto !important; + background-image: url('../img/file_menu_icon.png'); + width: 16px; + height: 16px; + position: absolute; + top: 50%; + margin-top: -8.5px; + font-style: normal; + background-size: auto !important; background-repeat: no-repeat; } .ace_config_editor_scroll ::-webkit-scrollbar { @@ -6965,18 +7030,18 @@ select[disabled]{ } /*外链分享 */ .checkbox_grourd input { - margin-right: 2px; - height: 16px; - width: 16px; + margin-right: 2px; + height: 16px; + width: 16px; } .checkbox_grourd { - height: 30px; - line-height: 32px; - margin-right: 20px; - font-size: 14px; + height: 30px; + line-height: 32px; + margin-right: 20px; + font-size: 14px; font-weight: initial; - color:#666; + color: #666; } .external_link button{ vertical-align: middle; @@ -7017,91 +7082,94 @@ select[disabled]{ } .php_info_group p { height: 35px; - line-height: 35px; - border: 1px solid #ddd; - border-bottom: none; - padding-left: 5px; - font-size: 14px; - background: #f1f1f1; + line-height: 35px; + border: 1px solid #ddd; + border-bottom: none; + padding-left: 5px; + font-size: 14px; + background: #f1f1f1; } .php_info_group:last-child{ margin-bottom: 15px; } -#btvideo{ - display: inline-block; - height: 360px; +#btvideo { + display: inline-block; + height: 360px; } -.video-list{ - display: inline-block; - width: 250px; - height: 360px; - overflow: auto; +.video-list { + display: inline-block; + width: 250px; + height: 360px; + overflow: auto; } -.video-list .video-avt{ - background-color: #e8e8e8; +.video-list .video-avt { + background-color: #e8e8e8; } -.movie_pay .layui-layer-content{ - overflow: hidden !important; +.movie_pay .layui-layer-content { + overflow: hidden !important; } .video-list .video-avt td a, -.video-list .video-avt td{ - color: #20a53a; +.video-list .video-avt td { + color: #20a53a; } -.video-list tr td{ - border-top: 0px; - border-bottom: 1px solid #ddd; +.video-list tr td { + border-top: 0px; + border-bottom: 1px solid #ddd; } -.video-list tr:hover{ - cursor: pointer; +.video-list tr:hover { + cursor: pointer; } -#btvideo video{ - outline: none; - padding: 0; - display: block; - background-color: #000; +#btvideo video { + outline: none; + padding: 0; + display: block; + background-color: #000; } .minText{ white-space: nowrap; } -.upload_tips_mask{ +.upload_tips_mask { position: absolute; z-index: 999; background-color: #fff; width: 100%; left: 0; top: 0; - height: 335px; - border: 2px dashed #e0e0e0; - text-align: center; + height: 335px; + border: 2px dashed #e0e0e0; + text-align: center; } .upload_file_body{ position: relative; } -.upload_file_body.active{ - height: 335px; - border: 2px dashed #e0e0e0; - text-align: center; +.upload_file_body.active { + height: 440px; + border: 2px dashed #e0e0e0; + text-align: center; } + .upload_tips_mask span, -.upload_file_body.active span{ - font-size: 35px; - color: #999; - position: absolute; - width: 100%; - left: 0; - top: 50%; - margin-top: -40px; +.upload_file_body.active span { + font-size: 35px; + color: #999; + position: absolute; + width: 100%; + left: 0; + top: 50%; + margin-top: -40px; } -.dropUpLoadFile{ - position: relative; - overflow: auto; - height: 303px; - padding-top:0; + +.dropUpLoadFile { + position: relative; + overflow: auto; + height: 405px; + padding-top: 0; } + .dropUpLoadFile::-webkit-scrollbar { /*滚动条整体样式*/ - width : 15px; /*高宽分别对应横竖滚动条的尺寸*/ + width: 15px; /*高宽分别对应横竖滚动条的尺寸*/ height: 10px; } .dropUpLoadFile::-webkit-scrollbar-thumb { @@ -7110,272 +7178,314 @@ select[disabled]{ box-shadow : inset 0 0 5px rgba(0, 0, 0, 0.2); background : #bbb; } + .dropUpLoadFile::-webkit-scrollbar-track { /*滚动条里面轨道*/ - box-shadow : inset 0 0 5px rgba(0, 0, 0, 0.2); + box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); border-radius: 0; - background : #ededed; + background: #ededed; } -.dropUpLoadFile li{ - height: 40px; - line-height: 40px; - border-bottom: 1px solid #ececec; - position: relative; + +.dropUpLoadFile li { + height: 40px; + line-height: 40px; + border-bottom: 1px solid #ececec; + position: relative; } -.dropUpLoadFileHead{ - border-bottom: 1px solid #dedede; + +.dropUpLoadFileHead { + border-bottom: 1px solid #dedede; } -.dropUpLoadFileHead li.fileTitle{ - border-color: #ccc; +.dropUpLoadFileHead li.fileTitle { + border-color: #ccc; } -.upload_btn_groud .file_upload_info{ - height: 40px; - line-height: 40px; - font-size: 12px; - color: #fff; - padding-left: 5px; - font-weight: 400; - color: #3c763d; - background-color: #dff0d8; - border: 1px solid #d6e9c6; - border-radius: 2px; - position: relative; +.upload_btn_groud .file_upload_info { + height: 40px; + line-height: 40px; + font-size: 12px; + color: #fff; + padding-left: 5px; + font-weight: 400; + color: #3c763d; + background-color: #dff0d8; + border: 1px solid #d6e9c6; + border-radius: 2px; + position: relative; } -.upload_btn_groud .file_upload_info .ico-tips-close{ - height: 22px; - width: 22px; - background: #90bb81; - display: inline-block; - border-radius: 50%; - position: absolute; - right: 10px; - top: 50%; - margin-top: -11px; - cursor: pointer; - transition: all 500ms; +.upload_btn_groud .file_upload_info .ico-tips-close { + height: 22px; + width: 22px; + background: #90bb81; + display: inline-block; + border-radius: 50%; + position: absolute; + right: 10px; + top: 50%; + margin-top: -11px; + cursor: pointer; + transition: all 500ms; } -.upload_btn_groud .file_upload_info .ico-tips-close:hover{ - background: #658859; +.upload_btn_groud .file_upload_info .ico-tips-close:hover { + background: #658859; } -.upload_btn_groud .file_upload_info .ico-tips-close:after{ - transform: rotate(45deg); +.upload_btn_groud .file_upload_info .ico-tips-close:after { + transform: rotate(45deg); } -.upload_btn_groud .file_upload_info .ico-tips-close:before{ - transform: rotate(-45deg); +.upload_btn_groud .file_upload_info .ico-tips-close:before { + transform: rotate(-45deg); } .upload_btn_groud .file_upload_info .ico-tips-close:after, -.upload_btn_groud .file_upload_info .ico-tips-close:before{ - content:''; - display: inline-block; - height: 12px; - width: 2px; - background:#ececec; - border-radius: 5px; - position: absolute; - left: 50%; - top: 50%; - margin-top: -6px; - margin-left: -1px; +.upload_btn_groud .file_upload_info .ico-tips-close:before { + content: ''; + display: inline-block; + height: 12px; + width: 2px; + background: #ececec; + border-radius: 5px; + position: absolute; + left: 50%; + top: 50%; + margin-top: -6px; + margin-left: -1px; } -.upload_btn_groud .file_upload_info i{ - font-style: initial; +.upload_btn_groud .file_upload_info i { + font-style: initial; } -.upload_btn_groud .file_upload_info span{ - margin-right: 10px; - font-size: 13px; - display: inline-block; - height: 38px; - line-height: 38px; - margin-right: 0; - padding-left: 6px; +.upload_btn_groud .file_upload_info span { + margin-right: 10px; + font-size: 13px; + display: inline-block; + height: 38px; + line-height: 38px; + margin-right: 0; + padding-left: 6px; } -.dropUpLoadFile li .fileItem{ - position: relative; - z-index: 999; +.dropUpLoadFile li .fileItem { + display: flex; } -.dropUpLoadFile li .fileLoading{ - content: ''; - width: 0%; - height: 40px; - background: #6fb9d621; - position: absolute; - left: 0; - top: 0; - transition: width 100ms; - z-index: 998; -} -.dropUpLoadFile li:hover{ - cursor: pointer; -} -.upload_btn_groud .dropdown-menu{ - width: 100%; - min-width: 120%; - font-size: 13px; +.dropUpLoadFile li .fileLoading { + content: ''; + width: 0%; + height: 40px; + background: #6fb9d621; + position: absolute; + left: 0; + top: 0; + transition: width 100ms; + z-index: 998; } -.upload_btn_groud .btn-group:hover .dropdown-menu{ - display: block; +.dropUpLoadFile li:hover { + cursor: pointer; } -.dropdown-menu:before{ - content: ''; - width: 100%; - position: absolute; - top: -5px; - height: 5px; + +.dropUpLoadFile li .cancel-btn { + position: absolute; + right: 15px; + display: none; } -.upload_btn_groud{ - margin-bottom: 5px; - height: 40px; - line-height: 40px; + +.dropUpLoadFile li .cancel-btn .glyphicon { + font-size: 16px; + vertical-align: middle; + margin-right: 4px; + color: #20a53a; } -.upload_btn_groud .dropdown-toggle{ - border-left-color: #3f51b5; + +.dropUpLoadFileHead { + border-bottom: 1px solid #dedede; } -.upload_btn_groud .btn-primary{ - background-color: #4592f0; - border-color: #367fa9; + +.dropUpLoadFileHead li.fileTitle { + border-color: #ccc; } -.dropUpLoadFileHead li.fileTitle span{ - color: #666; - font-size: 13px; - display: inline-block; - height: 35px; - line-height: 35px; - margin-right: 0; - padding-left: 10px; - font-weight: 500; + +.upload_btn_groud .dropdown-menu { + width: 100%; + min-width: 120%; + font-size: 13px; } -.dropUpLoadFileHead li.fileTitle span:nth-child(1){ - width: 50%; - padding-left: 15px; + +.upload_btn_groud .btn-group:hover .dropdown-menu { + display: block; } -.dropUpLoadFileHead li.fileTitle span:nth-child(2){ - width: 25%; +.dropdown-menu:before { + content: ''; + width: 100%; + position: absolute; + top: -5px; + height: 5px; } -.dropUpLoadFileHead li.fileTitle span:nth-child(3){ - width: 25%; +.upload_btn_groud { + width: 100%; + height: 40px; + margin-bottom: 5px; } -.dropUpLoadFile .fileItem > span{ - color: #666; - font-size: 12px; - vertical-align: top; - padding-left: 10px; - height: 40px; - line-height: 40px; - display: inline-block; +.upload_btn_groud .dropdown-toggle { + border-left-color: #3f51b5; } -.dropUpLoadFile li .filename{ - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: 50%; - padding-left: 15px; + +.upload_btn_groud .btn-primary { + background-color: #4592f0; + border-color: #367fa9; } + +.dropUpLoadFileHead li.fileTitle { + display: flex; +} + +.dropUpLoadFileHead li.fileTitle span { + color: #666; + font-size: 13px; + display: inline-block; + height: 35px; + line-height: 35px; + margin-right: 0; + padding-left: 10px; + font-weight: 500; +} +.dropUpLoadFileHead li.fileTitle span:nth-child(1) { + flex: 2; + padding-left: 15px; +} +.dropUpLoadFileHead li.fileTitle span:nth-child(2) { + flex: .5; +} + +.dropUpLoadFileHead li.fileTitle span:nth-child(3) { + flex: 1; +} + +.dropUpLoadFile .fileItem > span { + display: flex; + align-items: center; + color: #666; + font-size: 12px; + padding-left: 10px; + height: 40px; + line-height: 40px; +} + +.dropUpLoadFile li .filename { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 2; + padding-left: 15px; +} + +.dropUpLoadFile li .filename span { + width: 90%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .dropUpLoadFile li .filename .ico { - height: 27px; - line-height: 27px; - background-size: 27px; - width: 25px; - vertical-align: middle; - margin-right: 10px; - float: initial; + display: inline-block; + width: 26px; + height: 27px; + line-height: 27px; + background-size: 27px; + margin-right: 10px; + vertical-align: middle; + image-rendering: -webkit-optimize-contrast; } -.dropUpLoadFile li .filesize{ - color: #999; - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: 25%; +.dropUpLoadFile li .filesize { + color: #999; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: .5; } -.dropUpLoadFile li .fileStatus{ - width: 25%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; +.dropUpLoadFile li .fileStatus { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } -.dropUpLoadFile li em{ - font-style: normal; - color: #06F; - float: right; - margin-right: 10px; +.dropUpLoadFile li em { + font-style: normal; + color: #06F; + float: right; + margin-right: 10px; } -.upload_success{ - color:#20a53a !important; +.upload_success { + color: #20a53a !important; } -.upload_error{ - color:#F56C6C !important; +.upload_error { + color: #F56C6C !important; } -.upload_primary{ - color:#409EFF !important; +.upload_primary { + color: #409EFF !important; } -.upload_warning{ - color:#409EFF !important; +.upload_warning { + color: #409EFF !important; } /*安全风险*/ -.warning_scan_view{ - padding: 20px; - position: relative; - color: #666; +.warning_scan_view { + padding: 20px; + position: relative; + color: #666; } .warning_scan_head { - padding: 15px 10px 20px 10px; + padding: 15px 10px 20px 10px; } -.warning_scan_head span{ - display: inline-block; - width: 450px; +.warning_scan_head span { + display: inline-block; + width: 450px; font-size: 18px; margin-bottom: 5px; } -.warning_scan_head button{ - position: absolute; - right: 40px; - top: 35px; - border: none; - background: #20a53a; - border-radius: 20px; - height: 38px; - line-height: 38px; - font-size: 15px; - width: 110px; - color: #fff; - transition: all 500ms; +.warning_scan_head button { + position: absolute; + right: 40px; + top: 35px; + border: none; + background: #20a53a; + border-radius: 20px; + height: 38px; + line-height: 38px; + font-size: 15px; + width: 110px; + color: #fff; + transition: all 500ms; } -.warning_scan_head button:hover{ - background:#10952a; +.warning_scan_head button:hover { + background: #10952a; } .warning_scan_head button:focus, -.warning_scan_head button:active{ - outline: none; +.warning_scan_head button:active { + outline: none; } -.warning_scan_ps i{ - font-style: inherit; - color: red; - margin: 0 5px; +.warning_scan_ps i { + font-style: inherit; + color: red; + margin: 0 5px; } -.warning_scan_time{ - font-size: 15px !important; +.warning_scan_time { + font-size: 15px !important; } -.warning_scan_body .module_head{ - height: 42px; - line-height: 42px; - padding-left: 15px; - color: #555; - background: #f5f5f5; - font-size: 13px; - border-top: 1px solid #e8e8e8; +.warning_scan_body .module_head { + height: 42px; + line-height: 42px; + padding-left: 15px; + color: #555; + background: #f5f5f5; + font-size: 13px; + border-top: 1px solid #e8e8e8; } -.module_details_list{ - display: none; +.module_details_list { + display: none; } -.module_details_list.active{ - display: block; - height: 400px; - overflow: auto; - transition: height 500ms; +.module_details_list.active { + display: block; + height: 400px; + overflow: auto; + transition: height 500ms; } .module_details_list::-webkit-scrollbar { /*滚动条整体样式*/ @@ -7391,164 +7501,164 @@ select[disabled]{ box-shadow:inset 0 0 5px rgba(0, 0, 0, 0.2); background:#ededed; } -.module_details_item{ - position:relative; - border-bottom: 1px solid #ececec; +.module_details_item { + position: relative; + border-bottom: 1px solid #ececec; } -.module_details_item:hover .module_details_head{ - background: #effffa; +.module_details_item:hover .module_details_head { + background: #effffa; } -.module_details_item:hover .module_details_body{ - background: #f8fffd; +.module_details_item:hover .module_details_body { + background: #f8fffd; } -.module_details_item:before{ - /*content:'';*/ - display: block; - width: 100%; - height: 6px; - position: absolute; - bottom: 0; - background: -webkit-linear-gradient(top,rgba(255, 255, 255, 0),rgba(220, 220, 220, .3)); - background: -moz-linear-gradient(top,rgba(255, 255, 255, 0),rgba(220, 220, 220, .3)); - background: -o-linear-gradient(top,rgba(255, 255, 255, 0),rgba(220, 220, 220, .3)); - background: linear-gradient(top,rgba(255, 255, 255, 0),rgba(220, 220, 220, .3)); - clear: both; +.module_details_item:before { + /*content:'';*/ + display: block; + width: 100%; + height: 6px; + position: absolute; + bottom: 0; + background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0), rgba(220, 220, 220, .3)); + background: -moz-linear-gradient(top, rgba(255, 255, 255, 0), rgba(220, 220, 220, .3)); + background: -o-linear-gradient(top, rgba(255, 255, 255, 0), rgba(220, 220, 220, .3)); + background: linear-gradient(top, rgba(255, 255, 255, 0), rgba(220, 220, 220, .3)); + clear: both; } -.module_details_item:after{ - content:''; - display: block; - width: 100%; - height: 6px; - top: 40px; - position: absolute; - background: -webkit-linear-gradient(top,rgba(220, 220, 220, .3),rgba(255, 255, 255, 0)); - background: -moz-linear-gradient(top,rgba(220, 220, 220, .3),rgba(255, 255, 255, 0)); - background: -o-linear-gradient(top,rgba(220, 220, 220, .3),rgba(255, 255, 255, 0)); - background: linear-gradient(top,rgba(220, 220, 220, .3),rgba(255, 255, 255, 0)); - clear: both; +.module_details_item:after { + content: ''; + display: block; + width: 100%; + height: 6px; + top: 40px; + position: absolute; + background: -webkit-linear-gradient(top, rgba(220, 220, 220, .3), rgba(255, 255, 255, 0)); + background: -moz-linear-gradient(top, rgba(220, 220, 220, .3), rgba(255, 255, 255, 0)); + background: -o-linear-gradient(top, rgba(220, 220, 220, .3), rgba(255, 255, 255, 0)); + background: linear-gradient(top, rgba(220, 220, 220, .3), rgba(255, 255, 255, 0)); + clear: both; } -.warning_scan_body .module_head:last-child{ - border-top: none; +.warning_scan_body .module_head:last-child { + border-top: none; } -.module_item .module_head{ - cursor: pointer; +.module_item .module_head { + cursor: pointer; } -.module_title{ - font-size: 14px; +.module_title { + font-size: 14px; } .risk .module_num, .security .module_num, -.ignore .module_num{ - display: inline-block; - height: 20px; - line-height: 21px; - width: 20px; - text-align: center; - margin-left: 5px; - border-radius: 3px; - color: #fff; +.ignore .module_num { + display: inline-block; + height: 20px; + line-height: 21px; + width: 20px; + text-align: center; + margin-left: 5px; + border-radius: 3px; + color: #fff; } -.risk .module_num{ - background:red; +.risk .module_num { + background: red; } -.security .module_num{ - background:#20A53A; +.security .module_num { + background: #20A53A; } -.ignore .module_num{ - background:#FFBA66; +.ignore .module_num { + background: #FFBA66; } -.module_cut_show{ - float: right; - padding-right: 10px; - height: 40px; - line-height: 40px; +.module_cut_show { + float: right; + padding-right: 10px; + height: 40px; + line-height: 40px; } -.module_cut_show i{ - color: #bbb; - font-style: initial; - margin-right: 5px; +.module_cut_show i { + color: #bbb; + font-style: initial; + margin-right: 5px; } -.module_cut_show .glyphicon{ - color: #20a53a; +.module_cut_show .glyphicon { + color: #20a53a; } .module_details_item{ } -.module_details_head{ - height: 40px; - line-height: 40px; - font-size: 13px; - padding-left: 15px; - position: relative; - background: #fbfbfb; - clear: both; - /*border-top: 1px solid #ececec;*/ +.module_details_head { + height: 40px; + line-height: 40px; + font-size: 13px; + padding-left: 15px; + position: relative; + background: #fbfbfb; + clear: both; + /*border-top: 1px solid #ececec;*/ } -.module_details_head:before{ - content: ''; - display: inline-block; - height: 5px; - width: 5px; - border-radius: 2.5px; - position: absolute; - /* background-color: #666; */ - top: 50%; - margin-top: -3.5px; - left: 15px; +.module_details_head:before { + content: ''; + display: inline-block; + height: 5px; + width: 5px; + border-radius: 2.5px; + position: absolute; + /* background-color: #666; */ + top: 50%; + margin-top: -3.5px; + left: 15px; } -.operate_tools{ - float: right; - padding-right: 15px; - font-size: 12.5px; +.operate_tools { + float: right; + padding-right: 15px; + font-size: 12.5px; } -.operate_tools a{ - color: #9a9a9a; +.operate_tools a { + color: #9a9a9a; } -.operate_tools a.btlink{ - color: #20a53a; +.operate_tools a.btlink { + color: #20a53a; } -.module_details_title{ - font-weight: 500; +.module_details_title { + font-weight: 500; } -.module_details_title i{ - color: #bbb; - font-style: initial; - font-weight: 400; +.module_details_title i { + color: #bbb; + font-style: initial; + font-weight: 400; } -.module_details_body{ - padding: 10px 15px; - display: none; +.module_details_body { + padding: 10px 15px; + display: none; } -.module_details_body .module_details_line{ - line-height: 25px; - margin-bottom: 5px; - clear: both; +.module_details_body .module_details_line { + line-height: 25px; + margin-bottom: 5px; + clear: both; } .module_details_body .line_content, -.module_details_body .line_title{ - display: inline-block; - font-size: 12.5px; +.module_details_body .line_title { + display: inline-block; + font-size: 12.5px; } -.module_details_body .line_title{ - height: 100%; - width: 75px; - vertical-align: top; +.module_details_body .line_title { + height: 100%; + width: 75px; + vertical-align: top; color: #999; text-align: right; - padding-right: 5px; + padding-right: 5px; } -.module_details_body .line_content i{ - font-style:initial; +.module_details_body .line_content i { + font-style: initial; } -.module_details_body .line_content{ - width: 580px; - word-wrap:break-word; +.module_details_body .line_content { + width: 580px; + word-wrap: break-word; } -.module_details_block{ - display: inline-block; +.module_details_block { + display: inline-block; } -.module_details_block .line_content{ - width:250px; +.module_details_block .line_content { + width: 250px; } /*END*/ @@ -7567,16 +7677,16 @@ select[disabled]{ margin-bottom: 15px; display: none; } -.quick_links>span.glyphicon{ +.quick_links>span.glyphicon { position: absolute; - right: 12px; + right: 12px; top: 12px; color: #999; } -.quick_links_title{ +.quick_links_title { height: 35px; - line-height: 37px; - vertical-align: top; + line-height: 37px; + vertical-align: top; display: inline-block; padding: 0 0 0 15px; position: absolute; @@ -7592,11 +7702,11 @@ select[disabled]{ background: #f1f1f1; font-size: 0; } -.term_item_tab .list{ +.term_item_tab .list { display: inline-block; webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; + -moz-user-select: none; + -ms-user-select: none; user-select: none; /* display: flex; */ height: 38px; @@ -7621,11 +7731,11 @@ select[disabled]{ background-color:transparent; } -.term_item_tab .list>span{ - height: 38px; - line-height: 38px; - text-align: left; - padding: 0 25px 0 22px; +.term_item_tab .list>span { + height: 38px; + line-height: 38px; + text-align: left; + padding: 0 25px 0 22px; display: inline-block; vertical-align: middle; border-right: 1px solid #e2e2e2; @@ -7635,27 +7745,27 @@ select[disabled]{ flex-shrink: 0; } -.icon-trem-close{ +.icon-trem-close { height: 18px; - width: 18px; - position: absolute; - top: 50%; - margin-top: -8.5px; + width: 18px; + position: absolute; + top: 50%; + margin-top: -8.5px; right: 8px; display: none !important; } .icon-trem-close::after, -.icon-trem-close::before{ +.icon-trem-close::before { content: ''; - height: 14px; - width: 2px; - display: inline-block; - background: #fb0000; - z-index: 999; - position: absolute; - position: absolute; - top: 1.5px; - left: 8px; + height: 14px; + width: 2px; + display: inline-block; + background: #fb0000; + z-index: 999; + position: absolute; + position: absolute; + top: 1.5px; + left: 8px; transform: rotate(-45deg); } .icon-trem-close::after{ @@ -7673,16 +7783,16 @@ select[disabled]{ .term_item_tab .list>span i{ transition: all 50ms; } -.term_item_tab .list>span .icon{ +.term_item_tab .list>span .icon { height: 9px; - width: 9px; - border-radius: 5px; - display: inline-block; - background: #fff; - position: absolute; - left: 8px; - top: 50%; - margin-top:-4.5px; + width: 9px; + border-radius: 5px; + display: inline-block; + background: #fff; + position: absolute; + left: 8px; + top: 50%; + margin-top: -4.5px; } .term_item_tab .list>span .icon.icon-sucess{ background-color: #10952a; @@ -7711,12 +7821,12 @@ select[disabled]{ white-space: nowrap; text-overflow: ellipsis; } -.term_item_tab .item .glyphicon{ +.term_item_tab .item .glyphicon { vertical-align: top; - position: absolute; - right: 12px; - font-size: 15px; - height: 38px; + position: absolute; + right: 12px; + font-size: 15px; + height: 38px; line-height: 38px; color: #ff7070 !important; display: none; @@ -7893,12 +8003,12 @@ select[disabled]{ text-align: left; font-weight: 600; } -.tootls_content{ +.tootls_content { position: absolute; - top: 38px; - left: 0; - right: 0; - bottom: 0; + top: 38px; + left: 0; + right: 0; + bottom: 0; border-top: 1px solid #ececec; } .tootls_host_list, @@ -8016,22 +8126,22 @@ select[disabled]{ line-height: 34px; */ padding: 8px 12px; } -.menu_right_list{ - width: 140px; - position: fixed; - border: 1px solid #ccc; - border: 1px solid rgba(0,0,0,.15); - border-radius: 2px; - -webkit-box-shadow: 0 6px 12px rgba(0,0,0,.175); - box-shadow: 0 6px 12px rgba(0,0,0,.175); +.menu_right_list { + width: 140px; + position: fixed; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 2px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); z-index: 999999; background: #fff; padding: 6px 0; display: inline-block; - webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; + webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } .menu_right_list li{ height: 28px; @@ -8073,16 +8183,16 @@ select[disabled]{ margin-bottom: 15px; display: none; } -.quick_links>span.glyphicon{ +.quick_links>span.glyphicon { position: absolute; - right: 12px; + right: 12px; top: 12px; color: #999; } -.quick_links_title{ +.quick_links_title { height: 35px; - line-height: 37px; - vertical-align: top; + line-height: 37px; + vertical-align: top; display: inline-block; padding: 0 0 0 15px; position: absolute; @@ -8094,25 +8204,25 @@ select[disabled]{ margin-right: 4px; color: #ffa515; } -.term-move-border{ - width: 100%; - height: 5px; - cursor: n-resize; - margin-top: -5px; - opacity: 0; +.term-move-border { + width: 100%; + height: 5px; + cursor: n-resize; + margin-top: -5px; + opacity: 0; } -.term-tool-button{ - position: absolute; - right: 0px; - top: 50%; - width: 28px; - height: 60px; - background-color: #565656; - border-top-left-radius: 30px; - border-bottom-left-radius: 30px; - cursor: pointer; - z-index: 999; - margin-top: -30px; +.term-tool-button { + position: absolute; + right: 0px; + top: 50%; + width: 28px; + height: 60px; + background-color: #565656; + border-top-left-radius: 30px; + border-bottom-left-radius: 30px; + cursor: pointer; + z-index: 999; + margin-top: -30px; } .term-tool-button span{ font-size: 12px; @@ -8132,11 +8242,11 @@ select[disabled]{ background: #f1f1f1; font-size: 0; } -.term_item_tab .list{ +.term_item_tab .list { display: inline-block; webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; + -moz-user-select: none; + -ms-user-select: none; user-select: none; /* display: flex; */ height: 38px; @@ -8161,11 +8271,11 @@ select[disabled]{ background-color:transparent; } -.term_item_tab .list>span{ - height: 38px; - line-height: 38px; - text-align: left; - padding: 0 25px 0 22px; +.term_item_tab .list>span { + height: 38px; + line-height: 38px; + text-align: left; + padding: 0 25px 0 22px; display: inline-block; vertical-align: middle; border-right: 1px solid #e2e2e2; @@ -8175,27 +8285,27 @@ select[disabled]{ flex-shrink: 0; } -.icon-trem-close{ +.icon-trem-close { height: 18px; - width: 18px; - position: absolute; - top: 50%; - margin-top: -8.5px; + width: 18px; + position: absolute; + top: 50%; + margin-top: -8.5px; right: 8px; display: none !important; } .icon-trem-close::after, -.icon-trem-close::before{ +.icon-trem-close::before { content: ''; - height: 14px; - width: 2px; - display: inline-block; - background: #fb0000; - z-index: 999; - position: absolute; - position: absolute; - top: 1.5px; - left: 8px; + height: 14px; + width: 2px; + display: inline-block; + background: #fb0000; + z-index: 999; + position: absolute; + position: absolute; + top: 1.5px; + left: 8px; transform: rotate(-45deg); } .icon-trem-close::after{ @@ -8213,16 +8323,16 @@ select[disabled]{ .term_item_tab .list>span i{ transition: all 50ms; } -.term_item_tab .list>span .icon{ +.term_item_tab .list>span .icon { height: 9px; - width: 9px; - border-radius: 5px; - display: inline-block; - background: #fff; - position: absolute; - left: 8px; - top: 50%; - margin-top:-4.5px; + width: 9px; + border-radius: 5px; + display: inline-block; + background: #fff; + position: absolute; + left: 8px; + top: 50%; + margin-top: -4.5px; } .term_item_tab .list>span .icon.icon-sucess{ background-color: #10952a; @@ -8251,12 +8361,12 @@ select[disabled]{ white-space: nowrap; text-overflow: ellipsis; } -.term_item_tab .item .glyphicon{ +.term_item_tab .item .glyphicon { vertical-align: top; - position: absolute; - right: 12px; - font-size: 15px; - height: 38px; + position: absolute; + right: 12px; + font-size: 15px; + height: 38px; line-height: 38px; color: #ff7070 !important; display: none; @@ -8458,12 +8568,12 @@ select[disabled]{ text-align: left; font-weight: 600; } -.tootls_content{ +.tootls_content { position: absolute; - top: 38px; - left: 0; - right: 0; - bottom: 0; + top: 38px; + left: 0; + right: 0; + bottom: 0; border-top: 1px solid #ececec; } .tootls_host_list, @@ -8581,22 +8691,22 @@ select[disabled]{ line-height: 34px; */ padding: 8px 12px; } -.menu_right_list{ - width: 140px; - position: fixed; - border: 1px solid #ccc; - border: 1px solid rgba(0,0,0,.15); - border-radius: 2px; - -webkit-box-shadow: 0 6px 12px rgba(0,0,0,.175); - box-shadow: 0 6px 12px rgba(0,0,0,.175); +.menu_right_list { + width: 140px; + position: fixed; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 2px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); z-index: 999999; background: #fff; padding: 6px 0; display: inline-block; - webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; + webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } .menu_right_list li{ height: 28px; @@ -8622,52 +8732,52 @@ select[disabled]{ height: 1px; background-color: #ececec; } -.localhost-form-shade{ - position: absolute; - left: 0; - right: 0; - bottom:0; - top: 0; - z-index: 99999; - background: rgb(0 0 0 / 50%); +.localhost-form-shade { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: 99999; + background: rgb(0 0 0 / 50%); } -.localhost-form-view{ - padding-left: 25px; - padding-top: 40px; - width: 500px; - background: #fff; - top: 50%; - left: 50%; - position: absolute; - margin-left: -250px; - margin-top: -200px; - border-radius: 2px; - padding-bottom: 45px; +.localhost-form-view { + padding-left: 25px; + padding-top: 40px; + width: 500px; + background: #fff; + top: 50%; + left: 50%; + position: absolute; + margin-left: -250px; + margin-top: -200px; + border-radius: 2px; + padding-bottom: 45px; } -.localhost-form-view>button{ - margin-left: 100px; - margin-top: 10px; - width: 120px; - height: 36px; - font-size: 14px; +.localhost-form-view>button { + margin-left: 100px; + margin-top: 10px; + width: 120px; + height: 36px; + font-size: 14px; } -.localhost-form-title{ - font-size: 15px; - color: red; - text-align: center; - height: 60px; - line-height: 50px; +.localhost-form-title { + font-size: 15px; + color: red; + text-align: center; + height: 60px; + line-height: 50px; } -.localhost-form_tip{ - height: 30px; - width: 30px; - background-image: url(/static/layer/skin/default/icon.png); - background-size: auto; - display: inline-block; - background-position: -150px -0px; - vertical-align:middle; - margin-right: 15px; +.localhost-form_tip { + height: 30px; + width: 30px; + background-image: url(/static/layer/skin/default/icon.png); + background-size: auto; + display: inline-block; + background-position: -150px -0px; + vertical-align: middle; + margin-right: 15px; } /*end*/ @@ -8691,14 +8801,14 @@ select[disabled]{ .guide_content .line{ margin-bottom: 5px; } -.ssl_product_table{ +.ssl_product_table { position: relative; - display: inline-block; + display: inline-block; vertical-align: middle; width: 410px; } -.ssl_product_table tbody tr td:nth-child(1) input{ - height: 15px; +.ssl_product_table tbody tr td:nth-child(1) input { + height: 15px; width: 15px; margin-right: 5px !important; color: #20a532; @@ -8714,23 +8824,23 @@ select[disabled]{ vertical-align: text-top; margin: 0; } -.guide_path_progress{ +.guide_path_progress { margin: 0 150px; - height: 3px; - display: inline-block; - position: absolute; - top: 35px; + height: 3px; + display: inline-block; + position: absolute; + top: 35px; } -.guide_path_progress span{ - height: 3px; +.guide_path_progress span { + height: 3px; z-index: 980; display: inline-block; - position: absolute; - left: 0; + position: absolute; + left: 0; } .guide_path_progress span:nth-child(1) { width: 0; - background: #20a53a; + background: #20a53a; } .guide_path_progress span:nth-child(2) { width: 300px; @@ -8811,11 +8921,11 @@ select[disabled]{ height: 35px; line-height: 35px; } -.ssl_valid_year .pay-cycle-btn.disabled{ +.ssl_valid_year .pay-cycle-btn.disabled { border: 1px solid #DDD; - background-color: #F5F5F5; + background-color: #F5F5F5; color: #ACA899; - cursor:no-drop; + cursor: no-drop; } .guide_price{ color: #FF6232; @@ -8828,48 +8938,48 @@ select[disabled]{ } .ssl_class_table input { margin: 0; - margin-right: 5px; - height: 14px; - width: 14px; + margin-right: 5px; + height: 14px; + width: 14px; } .ssl_class_table span{ vertical-align: text-bottom; } -.ssl-brand-info{ +.ssl-brand-info { width: 410px; display: inline-block; - background-color: #efefef; + background-color: #efefef; border: 1px solid #ececec; border-radius: 5px; display: inline-block; background-color: #f5f5f5; color: #666; - z-index: 2; + z-index: 2; cursor: pointer; background-repeat: no-repeat; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAoCAYAAAAsTRLGAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAaiSURBVHja7Fp7kJZjFP+xqKSo5JrGJayoI6MaGZmhqcRi3EaRVrpsTdJlm6SLlOiiwVDKJeWS1WJcUmZoasK20ey21W50qlEZZIxaiZHL55/zzXzzzjnve773+8eM54/fP9/3O+e5nfc5tweZTAYBAV6ETQgIBhMQDCYgGExAMJiAYDABAcFgAoLBBASDCfgvGgyDLBzDoC4M6s+gMQwqZ9BYBWMYNEt4lq5jGVTCoPkMWsWgGgZtZ1ADg+oZtIFBrzFoJINOjdGTRTMGjWLQQwyaxKBSBrV0yGlrvFV0jGPQDAZ1dMhdImOvYFCVrKFB1lTLoJUMeoxB3ZzzuE3GHmtgHIPGM+g+BvVm0Okp1prF0Qzqw6C5DPqAQZtyzqFBzqJCxm0XldcUdmTQcgYdYlAmD8w3JjiZQX/kqetlBh0Xs+hLFZn9DDotj41rw6Cdip6bYmSuko3NZy1fMahrwlzeyVNnhkHMoInyMXrXPDrFua5g0AmWwcxOMfEMg75XvvA2YrGZlNgtOrSF9zJk3nJuXBMGbTN0nGfIzChgLRkGDUo4yLR6f2ZQJ8d6qwoYYz+D2kcN5oUYgd/ka6xlUJ1gK4N2MGg1gy5UJllr6Kpn0EwGlTFomHwl1QZ3pbEBZTFzvdNhMOsM2QMMaqHwJxn8Xxi0RA58qLjJZQw6YvAvipnT4wz6WvanLgebxYX/GLPmAwluaq0ht0suiZFyFuNjuF/kGswAg7SRQT3zvPbAoOmGvgkxMk8aMpcr3Gkxm/ctg5rHjFMZI7vZcNEat5pBTY0xLjNknigg9sjGTqvyDAms22t2zDjWB9InS6hR/vww5aKaM+gHRd8iR0zRqMiNUrivJlyhC40xFibIaWt+RuH9xKDWCet5UZH7XOFdLAc0mkH3MugMxx5rMU+twW1I4bqLGLRHkZsMBl1hbF6XlAZzo6Gv2CGrGe40hbfG4Xd75XErZbFYGYsV3kzHWsYYgaonbkzKroYYcUY0UbDO9krH/D9S5J6zrqxtBVybjyj6tjhltyiy5c6vRouVsvxhzuBuYmSccw3eNY61lCtyWxXeS0aWGKe71GkwDyi8vQw6yjH/TzQ3BvF9abMNDUsVfW845I6Xqz4qe7fiug4rvHWSMeT+NlgCTS3+2KX8XhoZq69hMOc41vO0M4j/VOEtSdA9QZGpUXhzFN5a5znuVmTvB4PeVf6YU4DBrFf0PeWQO984nO4RXrHBu1aJURoZdFDZ2M4M+k7R0SMy1iCFs08Kfmmu9OcV3nanG07KaN9WeNrZLnXMvbVRrymBZELRP8oKMJgdir4RDrk+zq+5n8E7UWooSW6nFYM6GP9doKS6WuaIlPHYgxFOWylZRHkD0sQXCk8rV0x3zL2rsT+dYWQ0/VIayynGBtzgkB2hyO1R/K3mv/+WsZOqplmX098oPjaJjLVE4b3prCL/6qgRFTsD9ig0dzpeKdZpt+hAZ6siKneIQW1gpLJ9UxpM+5QbAClBR+VWKbxZhmEV5VEDedgZkFakCEjBoOucmWJJntVmiDvVZK6P8FpJYTHKu8sx/8WKXFW2cFef8trS0FS+VE8tJVqL0DZhpDOr2JDAWe2ox7zvrNtsTBm/fKbwhhul/hYxulcadaEWzmxyasLc2xleYlLWYBbFHJYnuCuSDmhcoLVX/LUmf6bRBNwnXWlPDaZSSYf/kv++yW2eCT5WdCxQxrrd2JshMfthFQfvULjznNlOS+mqW/0vK6lYZhikdYOdJNVurfXQNmswxTE+/4gcZk2kv5HtcTQqh2U1Bg9H+i5jJR7403nFxhXStKyuRNxcB+W/OkcNJotNxvzWy1c3XD6ueWLkGvf1PCq2jQz6MqeHtDNmjzKSZFgtim4x57pczmCoFBlfYdDvBn9gtFvdi0H/pOxkznJWOfOB1UBsqdRavA3H3K/ooKKjp8E/y0gMvKiImUt1gftUz6CTE9Z7T4FjDLPew7Rm0LNGUcxCXcQd5aJ7iucN72mPdhLSvcoUDbx8+1zNErr51vOMmxP0zk15iNul7+Rdc6eYm9LCGu0VgvUKrYf46alyg8zIwaPye6kSG2joIllJpTTfqiXirpIsaIFUZNs6s7CpUnaf4jgQK6ibLNXSKc4MLjddHizGs0YC4CpZ0zq51idKpubVebXIlMe8uCuX2tgtDDq7gBpZR3kpWCEV5uxZbJBAfbGcu/lUIrxTDQiPwAOCwQQEgwkIBhMQDCYgIBhMQDCYgGAwAcFgAoLBBPyv8O8AvcYpbXUrBQ4AAAAASUVORK5CYII=); - background-size: 120px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAoCAYAAAAsTRLGAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAaiSURBVHja7Fp7kJZjFP+xqKSo5JrGJayoI6MaGZmhqcRi3EaRVrpsTdJlm6SLlOiiwVDKJeWS1WJcUmZoasK20ey21W50qlEZZIxaiZHL55/zzXzzzjnve773+8eM54/fP9/3O+e5nfc5tweZTAYBAV6ETQgIBhMQDCYgGExAMJiAYDABAcFgAoLBBASDCfgvGgyDLBzDoC4M6s+gMQwqZ9BYBWMYNEt4lq5jGVTCoPkMWsWgGgZtZ1ADg+oZtIFBrzFoJINOjdGTRTMGjWLQQwyaxKBSBrV0yGlrvFV0jGPQDAZ1dMhdImOvYFCVrKFB1lTLoJUMeoxB3ZzzuE3GHmtgHIPGM+g+BvVm0Okp1prF0Qzqw6C5DPqAQZtyzqFBzqJCxm0XldcUdmTQcgYdYlAmD8w3JjiZQX/kqetlBh0Xs+hLFZn9DDotj41rw6Cdip6bYmSuko3NZy1fMahrwlzeyVNnhkHMoInyMXrXPDrFua5g0AmWwcxOMfEMg75XvvA2YrGZlNgtOrSF9zJk3nJuXBMGbTN0nGfIzChgLRkGDUo4yLR6f2ZQJ8d6qwoYYz+D2kcN5oUYgd/ka6xlUJ1gK4N2MGg1gy5UJllr6Kpn0EwGlTFomHwl1QZ3pbEBZTFzvdNhMOsM2QMMaqHwJxn8Xxi0RA58qLjJZQw6YvAvipnT4wz6WvanLgebxYX/GLPmAwluaq0ht0suiZFyFuNjuF/kGswAg7SRQT3zvPbAoOmGvgkxMk8aMpcr3Gkxm/ctg5rHjFMZI7vZcNEat5pBTY0xLjNknigg9sjGTqvyDAms22t2zDjWB9InS6hR/vww5aKaM+gHRd8iR0zRqMiNUrivJlyhC40xFibIaWt+RuH9xKDWCet5UZH7XOFdLAc0mkH3MugMxx5rMU+twW1I4bqLGLRHkZsMBl1hbF6XlAZzo6Gv2CGrGe40hbfG4Xd75XErZbFYGYsV3kzHWsYYgaonbkzKroYYcUY0UbDO9krH/D9S5J6zrqxtBVybjyj6tjhltyiy5c6vRouVsvxhzuBuYmSccw3eNY61lCtyWxXeS0aWGKe71GkwDyi8vQw6yjH/TzQ3BvF9abMNDUsVfW845I6Xqz4qe7fiug4rvHWSMeT+NlgCTS3+2KX8XhoZq69hMOc41vO0M4j/VOEtSdA9QZGpUXhzFN5a5znuVmTvB4PeVf6YU4DBrFf0PeWQO984nO4RXrHBu1aJURoZdFDZ2M4M+k7R0SMy1iCFs08Kfmmu9OcV3nanG07KaN9WeNrZLnXMvbVRrymBZELRP8oKMJgdir4RDrk+zq+5n8E7UWooSW6nFYM6GP9doKS6WuaIlPHYgxFOWylZRHkD0sQXCk8rV0x3zL2rsT+dYWQ0/VIayynGBtzgkB2hyO1R/K3mv/+WsZOqplmX098oPjaJjLVE4b3prCL/6qgRFTsD9ig0dzpeKdZpt+hAZ6siKneIQW1gpLJ9UxpM+5QbAClBR+VWKbxZhmEV5VEDedgZkFakCEjBoOucmWJJntVmiDvVZK6P8FpJYTHKu8sx/8WKXFW2cFef8trS0FS+VE8tJVqL0DZhpDOr2JDAWe2ox7zvrNtsTBm/fKbwhhul/hYxulcadaEWzmxyasLc2xleYlLWYBbFHJYnuCuSDmhcoLVX/LUmf6bRBNwnXWlPDaZSSYf/kv++yW2eCT5WdCxQxrrd2JshMfthFQfvULjznNlOS+mqW/0vK6lYZhikdYOdJNVurfXQNmswxTE+/4gcZk2kv5HtcTQqh2U1Bg9H+i5jJR7403nFxhXStKyuRNxcB+W/OkcNJotNxvzWy1c3XD6ueWLkGvf1PCq2jQz6MqeHtDNmjzKSZFgtim4x57pczmCoFBlfYdDvBn9gtFvdi0H/pOxkznJWOfOB1UBsqdRavA3H3K/ooKKjp8E/y0gMvKiImUt1gftUz6CTE9Z7T4FjDLPew7Rm0LNGUcxCXcQd5aJ7iucN72mPdhLSvcoUDbx8+1zNErr51vOMmxP0zk15iNul7+Rdc6eYm9LCGu0VgvUKrYf46alyg8zIwaPye6kSG2joIllJpTTfqiXirpIsaIFUZNs6s7CpUnaf4jgQK6ibLNXSKc4MLjddHizGs0YC4CpZ0zq51idKpubVebXIlMe8uCuX2tgtDDq7gBpZR3kpWCEV5uxZbJBAfbGcu/lUIrxTDQiPwAOCwQQEgwkIBhMQDCYgIBhMQDCYgGAwAcFgAoLBBPyv8O8AvcYpbXUrBQ4AAAAASUVORK5CYII=); + background-size: 120px; background-position: 10px center; - padding:15px; - padding-left:150px; + padding: 15px; + padding-left: 150px; position: relative; } .ssl-brand-info::after{ content: ''; } -.prev-plan,.next-plan,.play-plan{ - padding: 5px 30px; - font-size: 14px; +.prev-plan,.next-plan,.play-plan { + padding: 5px 30px; + font-size: 14px; } .guide_content .line input[type=text]{ width: 400px; } -.ssl-info-line{ +.ssl-info-line { line-height: 32px; - height: 32px; - font-size: 15px; - color: #20a53a; + height: 32px; + font-size: 15px; + color: #20a53a; } .ssl_verification label{ height: 32px; @@ -8885,16 +8995,16 @@ select[disabled]{ .ssl_verification label span{ font-weight: 400; } -.ssl_applay_info{ +.ssl_applay_info { padding: 20px 0; - border-radius: 4px; + border-radius: 4px; } -.ssl_applay_info .payTitle{ +.ssl_applay_info .payTitle { margin-bottom: 15px; - height: 50px; - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALcAAAAyCAYAAAAeE0r0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RERDMENDNjk2OTNDMTFFNzhFQTlBQ0RDMzI3NzNCQzEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RERDMENDNkE2OTNDMTFFNzhFQTlBQ0RDMzI3NzNCQzEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpEREMwQ0M2NzY5M0MxMUU3OEVBOUFDREMzMjc3M0JDMSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpEREMwQ0M2ODY5M0MxMUU3OEVBOUFDREMzMjc3M0JDMSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pu+PnjYAABY7SURBVHja7F0JnBXFma+qfm9mOBS5ImIAbzFCNF7sBjcHaFhZQUXUqMGoYAh4RNlFophdiRFdNUHReLMeGFbQhSgBDS7giogBDEokGEWCgLCEQzniwMx0Vf7/7mqmp6f7vX5vBpjZfTW/b/qo7jr/9dX3ffVVP3nIMpEYpK4Q0mSFVhpHKYyoFtKLyAhhanBtcL9MCGWE0IhXrpCu00FKcSgePxyRXXH/SF7jLRNJ3sGNjchkudRqgzFiq1F6LTLYpbQjNCIVMjMK+SAx/iE93HC9hJQuR9xuEEuUBWlL6YJuqUXrhQeLTnd1E7qVm+odpZSQqIzWGmUy3rXXHLjnuu7ecwbG12tPxMXdT5tvXLrBfYagDOG4cPzeumtdJ+2c7aR16nrkSyttiCtzUJak9nMcp15cRjROOAKI6yON6o2CfQVZHAscthfGFjKhP71Y49gKiS1A6rs4Jb0HmisI/lIohSJDQ8DdCpDsBQCPkBpHKboEYJbFpdcBfOYsDISzCHkw5DU4zgFNAnF+qSl1VykUNOMV8c4hAOA1EEcWKlM2Fxx3MO51aeRycXwcCRoudWaxFOpF3Pp2qbtKYd+B2zjnS5GZKY3zOK5O2l+FhLx/HmgOxJ5puDyj1G2l0JjgPgz0iBLOVAgMZyYK0ftahDLyIii4c4TO3IIyVJS6rxQaBG7A+BvgmrNAP8Rl2YEvsmyjpBqPk1+jdEeVurAUigX3ICh5MwDsrzW5kkvRD3L/bIhK3zxAM0kpNFtw67IRUmenAjjtmnDxj5famYGTviWAl0IMuDOiDnFRRpddJnX5RHDHTFOvgBSirdJqOuTxc0vdWQp1lTRvdS8QsJUAJ+wLwPxSSJNpJnUwRpulGKaV3iKm66Tj4TV4WKsSAv5vgzsMBXMo6D5BW3bTx/QW0DyjzEMA6SLhLfJU2xXPFMtIXHAycn8XWormLz91BPUHvQN6fx+kzzUTduInBbRVa1Bn0DpQZa1YQu7lk0Rn343WP7mJN+461PhuI9z+RlVdArws8Pgw6+D7FvQClTc2KJVSI6SUF8TEfRX0E9CXc8p/St2Lw/frJZzg/xEKvRB/p/BWhHOGU5DHszgeF067wXKrUlHqBnoYdHY0rjHEZJR5ljFmnNZa0pckTDnCt0Cvg3rWlblrwd1bGXVlk5SqvSFsVmpjbsCRFbkFd5cYz5HKxzTunyR1i9lSqt8g/sw0yZoyk5Y3HItGfxiN/vWYRj4N9FNQtxzvH4T3/wV0fBKog+soATSXIt9bQW3inIZC9/6K54eArmgsYCeZGpiX8KbJRg90veiJOr1pKGyibkH9bFskDaIWwl+LKasLbnr0KVOBl+9qgly62sia+UbqqyB+0Bz5IEC5mt6IWlV7Hok474JuvBPawgJU/xzh+aiIG/BcmTcu4gh1llVSlK+qECZj8nEu5TjOPTj9DA39JOhItNU1oIPtI3tsR9dYQBLIo0DdeE1vNVBvC76FvE7ickFnhqiFnS2m4fx/k5UO79k/gX6BtG/EO4fuQ3A3gpJkRJQrW6ZxPWg1yj4jOisE9QlAHjPgaqJiTMZKgX1kE1rWRldVoVxTpZbThNozS5hyoLEGskHWF5MNK2ccadQVYN6jUOMekal+oFH6FKTxdmwGjhHO9oxoO72DMBU6X0d8F+mdh8YfSwDh1pW4vgPH10A7YkQMmk5/DvoD5UZ2Gu79I8634v0FOSeT+oDkrNAV790Oaol4DpIk/1wXz2zHcROe64fjhoTnKM+uBn1UZPfssSBqbM59Osr9Hdt2W3LNPgHAc4kqfIbgltLIwaJJrD6KzQD2czg+jdb7g/QaUflgNq41V3qj9u+krvgZqtA3sXJafT8R3J4fOrhBC1pXVC79k+LIAwAN07k3sLPYaVnnmLbZ8a5t/HKk8Q2rgG0vhLuhA3+A009Br4LOQDpTcyhZGvFsoCrQfbaxZHy/ywk4jguBh3rKQAvcXMG1ukUrvDsAZexgB0uuQHMcmcJzeWbIm9i2rutO8EagkzvZXAAP6pWR3ExgPM6SNrCCnyGJDo2l+EPsWCKNmQHRY5LQzl/8PtE+jt1yz9nbhhNQ1utwNQyR+QYjGl+MiXJXHzigcp3PqNJG+O62DjpxGKg6DSDD3MZOn1QIuyNulxX9snaA7LFK4lLQ82GxxHYQxZ9zDdvFmI2eMGXMSJF7R4axlEu7YzorInI/Z76hKbgx866wMi5993uK/KvcROn8MLijugPa6Uykdamt3+a0ymkI4Dvtre3h9s9gmj8ZU/th6TmKmAm2OB6y8EVKl4/2dscUL37MQxGfQlqzcbXNt0xyRw+kEolC+wIy/lO4Lrse3BYymeyWZlBJmqyMAUeSr8WJJe1eaC/kHpkkc38JjfQfOP4D6HLQiqg5DyHgctW2QassOPfYa22P5+AerQDkqF+3ADoc1N2a0rYkdPqFNMmi4x601+TgU4rZyZNrMNowHfS7lODuibo8gXcfxvmzKcCdCTOY8AAOgZ86zXKk+ysrX9Oq1K8AgHvmQzsjbbZRVRlg8/TCjDXQYoVegnou0VIvRFEmUi4swMwLEIs5UIUfw8D4HXhHZd1HDGWFoM34D0qivEuZTM9a5pQqlHnTrdT1wA15XFSsK0eU9LbKxYSvgM62Jr4ptgP+3oKcheO2uctAa9Cg1PCzaOBB6LTjQZ1xz9v3htAC96l8TqDcHHBWhAtx/1eU53H9QQzY2iN+iLXZLkvieA0EdDh8Ziltu3LmWV+M3B7O3wLzR2xbtNVkxO2w4kh32/5pOXi5BdfpoQFaSXAX5pdt5PnQ5aDoOEsBxJe00u9CpLhHmszFebj0n6EUvoySPI003vX3ZEaBr0KzqzwVz9yEgl9evNXUfJX7OuvL40ZsvHatOGJYd2HilZI3acdF3m/YfXtd0OhzcX4dzrkFbj3iR9pCM4MVePYSyenGD8vw3Oc4nkeg4r2lAUe3ogu9GXdZru0VMCw7Iv58az/finyDmSKIHm4VTV1waxjziVXYKuPEqKR7kRAAKVuIiBatow2ngsYGikBgRUHb3kJzb9rBAhqAd6ZbU+LSWrHEiI6FWI2kLyeMgHw8VPjr3WiwmiHGZN9CVnfKyGIDMl6JN55CEV6CHPChDLth0/QRWbPDc+2lKRsljTPUcsiGCPPthOtk6023nHtQjM/P3epZTHRrNzoh1KDcbwRTKNqdit0OmvJw/BD3zskBLqswiB1o8Gn23tERObwV0tgclCvCTamAjo5OdyFFiUrcEUVYK9DXZndUjAhAGJQhBbhrQrpXPtBFbfZh7k2l9H6cZ+2s4UTfy1eWYDAExhBcl9dVKKU8rvBlFXmRkWY8+vBjy2UhT5oHjNo937dimBNAG4yRTyDyZbNX5goh2UGh3DoacQViLkTat2EW6N5IC0AZZJepBwTOGlktdvXeKQ56o41wdjmYjBIXSKjYjcQ5FaI/2qg1KbgWzYVHWlnylLCdFvEn4nQt6IuYKfY6a0OnDNwzpjz0Zb9bFK7NB42v40AYea61BZuJUVgPCT1zcJJFBvR5eODGrMQ+ieszAU6aVkeK0H7egMMH70QHSo6ZoM4AzeSbXhJX3LQYDtn15lqdwKvRciiDA5F2Oyn1NuFWxLSPa60fYWCrb+HmKKXVgEZ2vTBJIJCuFC44dnWnauF8kPE/NBEjG4LbDKeJDdf32AbPWpm8IoF7Zy1wO+OdSaANSGMwjmQk5HpZHI/GNf1hdkfMV8fg2X9DHBW2XTjvFVMmk49rNjC0FXScgx5l6yojlrI23kKZlDdapTdqs+PKYhXi3+LKakxZOTNR5KC+8QhosuCiW/wg6USzL+g3SKsm/DmNPMvxezXZohpKGvVdYaoeN9JdZezKn6wt37YwunzbAe3JFb4lpHZmpJXmX8GpL5VeozW6T1HinMZ1oYrVFaLlO62F2746KeuvoSNGoFGXW8AGJsLHcL9LDtMXrRq34BmuSl5KUxs6o6W1GpRDjj4O8U9HuBFXie/GPZoex0VEkzB35RJ+x2JkbtBOqxTn+pIA5XGa5g5Hni9au7mKWEwW2kGcjRFZwNjkP3HgBopCSHwIrEgDQK/gfAyOOoetnDP4C8JfuXw0LaiDPMmR/oQMTi0CN12A1AFS6An+ZbXFUibEMNFxVOi8uIrwTHcwRIMhSsofizwORw1k3C5GoY6DPAWpsvVlQrd0k4DtgEv8wk69u0JT+A7LaVokzAoEwibQXyxo19tOPwb0e9rqrdnqw8g0TfCfgPR/YuX7TMQWHJzSj+XiImRuDpp3QGfnWUyqxDN3Iv9HcPxPXM9Iu5RvsfxjghsgvC08AEODk6bTO1zXXYbjzjwy9QKWgdYm0Gs4/7ggJQMv7Sh2xpe67Fojs0/s7XyCWFV7ADeai58moi+iriaLAaFuhJDbZ597f0qzSSi3KsZuINRuR7R9MXn5He0yFsQpcbUdmYH8xyl3cVKnRJ19rKlvI7j1WRbcA2iLRdyqCFfbBgBfYuPqzTohcNDuPcty1EIVys1xcn5MHWjjp4luHGh2yHafr85HoZ6jUR/qJ4tzZPHrtAuGdgakB+K9GBCXhUW5/BWW5gNpivwmiBRH4/2Bvi2Y7FB5n13zmVcmIvp6ytHNymQG+2DZD27NGtzR1J/xdEaLjk91sv7csZ89G4TGHIeGHW85aL8IwMpwvyzmZSZI8FeFQLsW13R66ku5Hen2IQdC3AcxJX4/rO1HFyts/m9a2peB5R+Jss7D8d9BNwbiQHj1MGxhsbb+Z61IOiafwpvC1TdI+1PkfbNNu79dcEq7JONp5Q1gjoqyYataEKu6VhEhOwuT4ehbgOp8L+CC+yFUoSiLPOU1So6ESFKeS2rtjUalrXqsqPUlDIcf2cWVFdaCEhA588gIQJnL21Ys6WfNgq/QrBvt4LDClAYYhVLSu7F8Qev5BBXiWdfL4zh1oDNYz8nH6diFe5SPNzSqcGnMVNAc5PFza6VJB25OsQWsTsWJrz3QTf24sigjO1sMG8Wo2dKUjcdVG7Efg6ETljLzQSKWZE7wPIjGHBiyfkQDfbe7ovPngGZammUb/ugo90VaU2jeAwaon7TE9X8V0cENl9LyAD8mT66sckn8Uev8lZQuzZNXoA2GgV5N+ghodJAVOJP8jBYovHdrWq5PzwrKlIukz/KLCfxYztUY69ND9hEqqOOV9FaM1IHYWYX6/BZiSaxc6uxWYuuwTeKw27v6fLV+O62JTsExYQsa/Lqw7Itr+nCXhe209nwJ4t7lUj2Oz4DqiST5vOBCgQtIE0StF2dalFCppX50TxLwYiwRfOdK0DOIn4Nyc8X1t+G1CdD9oB/g3R/S9Bnb3qhbuE2KDFQu6ed9E02luP5zvgFPeXIPHpki/aXLsiKB1B8AnwjevRgy7kmgG/xt9AcsVGtV81CiUxfuVx1qEmGRYpWOCW9BQ5sYQASmVXoDLkfHVtpOoAhDcD9fb/osbItWe9Cx5Kg4fijyOy4Z4e8Eol26axJHjyjB4ZmC9uVhduMFuTJdUx+wyiNBdira4CoO2kLl6UJnLiqXaKtVtLKk47rKczOYbkzFWMjPJxTFZT2fcHW93GvmPbB7YNEIrwitVuRqKLUHVacriIrnfUlKnfAXYbpZP42aEDCkXcCgHXeo9VC7GNcEBPc3XmXToEWkztSdYjBFA1f/HspjkQjLz+XI/8K4muaoZ3gJvNKuJK4E0VecfjfHWv+Zb+P4++h7cd/wLqKe0UBufVvaNBR3uIAqpaFrZWOA8oBv7q400tyHmaRq7w73BCpCBiXRJ+J0K7qY0N4+bgnjQswg0JNUIrkKCVDweYoiG3F+A845zQ9poDwtRf4NwyK06HGw5fAN6Rw6NlGJ5sJUfzt7UFT5KE3bNabuEKpXHnDbhSb09WOg2aKZBwD7OeG4C0QGgzYH6dZVBX3jNmQd+CYuO3F2CL70bzuVK5Z0+ueqK1cfL0b8FwD904g7Edc3gzwbNe7RTfjkQjsrX9milC/NlByQjlrf44oiys3N1+8h7dGgl3E9FsTFmH8W/uLU/tOpUpRd+R6M/AkO77dBbgU6NjRbYBvzR+H7M+R+LmtEx+c7eZsVCvxSfkdOy9x2BvqfAFh2g+sGLtOD7sf5UNDn6Hgu0w/G+XiCwYJ4OM7/Cnn1xbAMnAaMUeYV4/SUZEsu1MWCuhgXsB7C+4tAkzl4kdcQWpFAbAOKXPSZ2cY2QV3ZHjTZcfW0Tb6B2FiWn5DiW89km6lbb/Me3hiF288JITLNC9pyu3G+uFpIvSXn7Muf72kJNf/jCkhjMu/u99o+8Tj07SCaAOlT/UVkqt3JzuYRz3CTwi9xpKJ1L+XE0LPcUUPf73kAOE2I3N2+ughLQiEPqzzP01nqcLtjn5+luwTlb4uybsX5UpSRO+pf56AMlXGPNWnOxLEPnr+WG5NBBD1B9d/C30TNn4HhIha3ym0rRvZOsiQFeyiRHj0VK6kbRMBdGZrSvQ82TRWmZQ+lnduaEbKrtDTXcGdPPrGS7uStlx4kMlvKPQ6eIlBuq7CdMRNEh595Cc8GWjxl3DPQ6I+i8cfEjJSFuD8EnUO3z9MCcMfkm6TUtLUutetFfq9OF/nQb6VLeMdDCFz8UtPL9C+y99bhMYqni3B8HfdW5Gt7Ksi0b1P8oj3cui30sbvwg5mD/jgP52jjTIHix0g7IANleTXKsCo6/cSpDndoZdop7fnZNnlpBGN3NAr9Qqq5HFJYq6VtRPbTclHTtjqNivW65ToE5asp2fwm0CCR45NgiJ/mui4//7U0oSPfECHnrJDIspJffKJ5LzDlpigPHaVog54bo9iRLdLjkRsrKFpQvFpXZF+ssPSIFbk6otw97MBZlgOoi3PlmSDG9AHxWzb0P3kL+fDrBHUcwmT8T/VxoEsljR4jdeanTVhE2QlgD/M6hg2ncn1xwbIQQKLD5M6iw5Qv4zz9b0gFsmKwINGYG3XT2IcL+Xm9YpXSsI07vBMm7pi2/Pa7LWksMXX20qVok7CMreP0lRytJLWRVXdBfLpahHZoNxl2LfRH2qm6AM0yTZRCszdyFWGmDHYV6VyKRh6dRU420pyPcbW2iTQE2CZdJnVfKMlzD8CXWkuhmQSVcoxw58WrTWB4rzDSvcIofQFAva7oX7wshf8XIROvbAfb9JQw3MhLNwzjVBw4UBv+bPZESGRUGteWIF0KKcGdSQC3VaS9j+Tws2vixP27su75LayEOjIJYH6pKcr9pdDEwS3dVK6WPYHwpK9KfQKZ/BVpZA2OXTEAekC/ParIHxFYBUXxfbz3tlFiHr/BLbRT6qVSKJJzp5G6jTgN1D5yr8oodyJAPMnfqubQr4O/znAMIrsKWdVNmGx3MH5uz/+S8e2ee21vUoo1xvduW4NxsE5q+TGOazFGVnliB3+SR5Z+oawUGgBuk5+7crdOLzy291tnhi6bQtyO08Uyso9VeN+PMx8ZehuarJC1W+LLI6x8D3ene/t3SkJ0KewLcCs3L7Lo5fYdH8PmHdCDwPsz+ZOWYcBXi33zMxOlUArJ4NZOXs69HcrkUxRDhDJPAqubSr9nWgrNIfxNgAEAtFu/YPhnJ+0AAAAASUVORK5CYII=) no-repeat center center; + height: 50px; + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALcAAAAyCAYAAAAeE0r0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RERDMENDNjk2OTNDMTFFNzhFQTlBQ0RDMzI3NzNCQzEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RERDMENDNkE2OTNDMTFFNzhFQTlBQ0RDMzI3NzNCQzEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpEREMwQ0M2NzY5M0MxMUU3OEVBOUFDREMzMjc3M0JDMSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpEREMwQ0M2ODY5M0MxMUU3OEVBOUFDREMzMjc3M0JDMSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pu+PnjYAABY7SURBVHja7F0JnBXFma+qfm9mOBS5ImIAbzFCNF7sBjcHaFhZQUXUqMGoYAh4RNlFophdiRFdNUHReLMeGFbQhSgBDS7giogBDEokGEWCgLCEQzniwMx0Vf7/7mqmp6f7vX5vBpjZfTW/b/qo7jr/9dX3ffVVP3nIMpEYpK4Q0mSFVhpHKYyoFtKLyAhhanBtcL9MCGWE0IhXrpCu00FKcSgePxyRXXH/SF7jLRNJ3sGNjchkudRqgzFiq1F6LTLYpbQjNCIVMjMK+SAx/iE93HC9hJQuR9xuEEuUBWlL6YJuqUXrhQeLTnd1E7qVm+odpZSQqIzWGmUy3rXXHLjnuu7ecwbG12tPxMXdT5tvXLrBfYagDOG4cPzeumtdJ+2c7aR16nrkSyttiCtzUJak9nMcp15cRjROOAKI6yON6o2CfQVZHAscthfGFjKhP71Y49gKiS1A6rs4Jb0HmisI/lIohSJDQ8DdCpDsBQCPkBpHKboEYJbFpdcBfOYsDISzCHkw5DU4zgFNAnF+qSl1VykUNOMV8c4hAOA1EEcWKlM2Fxx3MO51aeRycXwcCRoudWaxFOpF3Pp2qbtKYd+B2zjnS5GZKY3zOK5O2l+FhLx/HmgOxJ5puDyj1G2l0JjgPgz0iBLOVAgMZyYK0ftahDLyIii4c4TO3IIyVJS6rxQaBG7A+BvgmrNAP8Rl2YEvsmyjpBqPk1+jdEeVurAUigX3ICh5MwDsrzW5kkvRD3L/bIhK3zxAM0kpNFtw67IRUmenAjjtmnDxj5famYGTviWAl0IMuDOiDnFRRpddJnX5RHDHTFOvgBSirdJqOuTxc0vdWQp1lTRvdS8QsJUAJ+wLwPxSSJNpJnUwRpulGKaV3iKm66Tj4TV4WKsSAv5vgzsMBXMo6D5BW3bTx/QW0DyjzEMA6SLhLfJU2xXPFMtIXHAycn8XWormLz91BPUHvQN6fx+kzzUTduInBbRVa1Bn0DpQZa1YQu7lk0Rn343WP7mJN+461PhuI9z+RlVdArws8Pgw6+D7FvQClTc2KJVSI6SUF8TEfRX0E9CXc8p/St2Lw/frJZzg/xEKvRB/p/BWhHOGU5DHszgeF067wXKrUlHqBnoYdHY0rjHEZJR5ljFmnNZa0pckTDnCt0Cvg3rWlblrwd1bGXVlk5SqvSFsVmpjbsCRFbkFd5cYz5HKxzTunyR1i9lSqt8g/sw0yZoyk5Y3HItGfxiN/vWYRj4N9FNQtxzvH4T3/wV0fBKog+soATSXIt9bQW3inIZC9/6K54eArmgsYCeZGpiX8KbJRg90veiJOr1pKGyibkH9bFskDaIWwl+LKasLbnr0KVOBl+9qgly62sia+UbqqyB+0Bz5IEC5mt6IWlV7Hok474JuvBPawgJU/xzh+aiIG/BcmTcu4gh1llVSlK+qECZj8nEu5TjOPTj9DA39JOhItNU1oIPtI3tsR9dYQBLIo0DdeE1vNVBvC76FvE7ickFnhqiFnS2m4fx/k5UO79k/gX6BtG/EO4fuQ3A3gpJkRJQrW6ZxPWg1yj4jOisE9QlAHjPgaqJiTMZKgX1kE1rWRldVoVxTpZbThNozS5hyoLEGskHWF5MNK2ccadQVYN6jUOMekal+oFH6FKTxdmwGjhHO9oxoO72DMBU6X0d8F+mdh8YfSwDh1pW4vgPH10A7YkQMmk5/DvoD5UZ2Gu79I8634v0FOSeT+oDkrNAV790Oaol4DpIk/1wXz2zHcROe64fjhoTnKM+uBn1UZPfssSBqbM59Osr9Hdt2W3LNPgHAc4kqfIbgltLIwaJJrD6KzQD2czg+jdb7g/QaUflgNq41V3qj9u+krvgZqtA3sXJafT8R3J4fOrhBC1pXVC79k+LIAwAN07k3sLPYaVnnmLbZ8a5t/HKk8Q2rgG0vhLuhA3+A009Br4LOQDpTcyhZGvFsoCrQfbaxZHy/ywk4jguBh3rKQAvcXMG1ukUrvDsAZexgB0uuQHMcmcJzeWbIm9i2rutO8EagkzvZXAAP6pWR3ExgPM6SNrCCnyGJDo2l+EPsWCKNmQHRY5LQzl/8PtE+jt1yz9nbhhNQ1utwNQyR+QYjGl+MiXJXHzigcp3PqNJG+O62DjpxGKg6DSDD3MZOn1QIuyNulxX9snaA7LFK4lLQ82GxxHYQxZ9zDdvFmI2eMGXMSJF7R4axlEu7YzorInI/Z76hKbgx866wMi5993uK/KvcROn8MLijugPa6Uykdamt3+a0ymkI4Dvtre3h9s9gmj8ZU/th6TmKmAm2OB6y8EVKl4/2dscUL37MQxGfQlqzcbXNt0xyRw+kEolC+wIy/lO4Lrse3BYymeyWZlBJmqyMAUeSr8WJJe1eaC/kHpkkc38JjfQfOP4D6HLQiqg5DyHgctW2QassOPfYa22P5+AerQDkqF+3ADoc1N2a0rYkdPqFNMmi4x601+TgU4rZyZNrMNowHfS7lODuibo8gXcfxvmzKcCdCTOY8AAOgZ86zXKk+ysrX9Oq1K8AgHvmQzsjbbZRVRlg8/TCjDXQYoVegnou0VIvRFEmUi4swMwLEIs5UIUfw8D4HXhHZd1HDGWFoM34D0qivEuZTM9a5pQqlHnTrdT1wA15XFSsK0eU9LbKxYSvgM62Jr4ptgP+3oKcheO2uctAa9Cg1PCzaOBB6LTjQZ1xz9v3htAC96l8TqDcHHBWhAtx/1eU53H9QQzY2iN+iLXZLkvieA0EdDh8Ziltu3LmWV+M3B7O3wLzR2xbtNVkxO2w4kh32/5pOXi5BdfpoQFaSXAX5pdt5PnQ5aDoOEsBxJe00u9CpLhHmszFebj0n6EUvoySPI003vX3ZEaBr0KzqzwVz9yEgl9evNXUfJX7OuvL40ZsvHatOGJYd2HilZI3acdF3m/YfXtd0OhzcX4dzrkFbj3iR9pCM4MVePYSyenGD8vw3Oc4nkeg4r2lAUe3ogu9GXdZru0VMCw7Iv58az/finyDmSKIHm4VTV1waxjziVXYKuPEqKR7kRAAKVuIiBatow2ngsYGikBgRUHb3kJzb9rBAhqAd6ZbU+LSWrHEiI6FWI2kLyeMgHw8VPjr3WiwmiHGZN9CVnfKyGIDMl6JN55CEV6CHPChDLth0/QRWbPDc+2lKRsljTPUcsiGCPPthOtk6023nHtQjM/P3epZTHRrNzoh1KDcbwRTKNqdit0OmvJw/BD3zskBLqswiB1o8Gn23tERObwV0tgclCvCTamAjo5OdyFFiUrcEUVYK9DXZndUjAhAGJQhBbhrQrpXPtBFbfZh7k2l9H6cZ+2s4UTfy1eWYDAExhBcl9dVKKU8rvBlFXmRkWY8+vBjy2UhT5oHjNo937dimBNAG4yRTyDyZbNX5goh2UGh3DoacQViLkTat2EW6N5IC0AZZJepBwTOGlktdvXeKQ56o41wdjmYjBIXSKjYjcQ5FaI/2qg1KbgWzYVHWlnylLCdFvEn4nQt6IuYKfY6a0OnDNwzpjz0Zb9bFK7NB42v40AYea61BZuJUVgPCT1zcJJFBvR5eODGrMQ+ieszAU6aVkeK0H7egMMH70QHSo6ZoM4AzeSbXhJX3LQYDtn15lqdwKvRciiDA5F2Oyn1NuFWxLSPa60fYWCrb+HmKKXVgEZ2vTBJIJCuFC44dnWnauF8kPE/NBEjG4LbDKeJDdf32AbPWpm8IoF7Zy1wO+OdSaANSGMwjmQk5HpZHI/GNf1hdkfMV8fg2X9DHBW2XTjvFVMmk49rNjC0FXScgx5l6yojlrI23kKZlDdapTdqs+PKYhXi3+LKakxZOTNR5KC+8QhosuCiW/wg6USzL+g3SKsm/DmNPMvxezXZohpKGvVdYaoeN9JdZezKn6wt37YwunzbAe3JFb4lpHZmpJXmX8GpL5VeozW6T1HinMZ1oYrVFaLlO62F2746KeuvoSNGoFGXW8AGJsLHcL9LDtMXrRq34BmuSl5KUxs6o6W1GpRDjj4O8U9HuBFXie/GPZoex0VEkzB35RJ+x2JkbtBOqxTn+pIA5XGa5g5Hni9au7mKWEwW2kGcjRFZwNjkP3HgBopCSHwIrEgDQK/gfAyOOoetnDP4C8JfuXw0LaiDPMmR/oQMTi0CN12A1AFS6An+ZbXFUibEMNFxVOi8uIrwTHcwRIMhSsofizwORw1k3C5GoY6DPAWpsvVlQrd0k4DtgEv8wk69u0JT+A7LaVokzAoEwibQXyxo19tOPwb0e9rqrdnqw8g0TfCfgPR/YuX7TMQWHJzSj+XiImRuDpp3QGfnWUyqxDN3Iv9HcPxPXM9Iu5RvsfxjghsgvC08AEODk6bTO1zXXYbjzjwy9QKWgdYm0Gs4/7ggJQMv7Sh2xpe67Fojs0/s7XyCWFV7ADeai58moi+iriaLAaFuhJDbZ597f0qzSSi3KsZuINRuR7R9MXn5He0yFsQpcbUdmYH8xyl3cVKnRJ19rKlvI7j1WRbcA2iLRdyqCFfbBgBfYuPqzTohcNDuPcty1EIVys1xcn5MHWjjp4luHGh2yHafr85HoZ6jUR/qJ4tzZPHrtAuGdgakB+K9GBCXhUW5/BWW5gNpivwmiBRH4/2Bvi2Y7FB5n13zmVcmIvp6ytHNymQG+2DZD27NGtzR1J/xdEaLjk91sv7csZ89G4TGHIeGHW85aL8IwMpwvyzmZSZI8FeFQLsW13R66ku5Hen2IQdC3AcxJX4/rO1HFyts/m9a2peB5R+Jss7D8d9BNwbiQHj1MGxhsbb+Z61IOiafwpvC1TdI+1PkfbNNu79dcEq7JONp5Q1gjoqyYataEKu6VhEhOwuT4ehbgOp8L+CC+yFUoSiLPOU1So6ESFKeS2rtjUalrXqsqPUlDIcf2cWVFdaCEhA588gIQJnL21Ys6WfNgq/QrBvt4LDClAYYhVLSu7F8Qev5BBXiWdfL4zh1oDNYz8nH6diFe5SPNzSqcGnMVNAc5PFza6VJB25OsQWsTsWJrz3QTf24sigjO1sMG8Wo2dKUjcdVG7Efg6ETljLzQSKWZE7wPIjGHBiyfkQDfbe7ovPngGZammUb/ugo90VaU2jeAwaon7TE9X8V0cENl9LyAD8mT66sckn8Uev8lZQuzZNXoA2GgV5N+ghodJAVOJP8jBYovHdrWq5PzwrKlIukz/KLCfxYztUY69ND9hEqqOOV9FaM1IHYWYX6/BZiSaxc6uxWYuuwTeKw27v6fLV+O62JTsExYQsa/Lqw7Itr+nCXhe209nwJ4t7lUj2Oz4DqiST5vOBCgQtIE0StF2dalFCppX50TxLwYiwRfOdK0DOIn4Nyc8X1t+G1CdD9oB/g3R/S9Bnb3qhbuE2KDFQu6ed9E02luP5zvgFPeXIPHpki/aXLsiKB1B8AnwjevRgy7kmgG/xt9AcsVGtV81CiUxfuVx1qEmGRYpWOCW9BQ5sYQASmVXoDLkfHVtpOoAhDcD9fb/osbItWe9Cx5Kg4fijyOy4Z4e8Eol26axJHjyjB4ZmC9uVhduMFuTJdUx+wyiNBdira4CoO2kLl6UJnLiqXaKtVtLKk47rKczOYbkzFWMjPJxTFZT2fcHW93GvmPbB7YNEIrwitVuRqKLUHVacriIrnfUlKnfAXYbpZP42aEDCkXcCgHXeo9VC7GNcEBPc3XmXToEWkztSdYjBFA1f/HspjkQjLz+XI/8K4muaoZ3gJvNKuJK4E0VecfjfHWv+Zb+P4++h7cd/wLqKe0UBufVvaNBR3uIAqpaFrZWOA8oBv7q400tyHmaRq7w73BCpCBiXRJ+J0K7qY0N4+bgnjQswg0JNUIrkKCVDweYoiG3F+A845zQ9poDwtRf4NwyK06HGw5fAN6Rw6NlGJ5sJUfzt7UFT5KE3bNabuEKpXHnDbhSb09WOg2aKZBwD7OeG4C0QGgzYH6dZVBX3jNmQd+CYuO3F2CL70bzuVK5Z0+ueqK1cfL0b8FwD904g7Edc3gzwbNe7RTfjkQjsrX9milC/NlByQjlrf44oiys3N1+8h7dGgl3E9FsTFmH8W/uLU/tOpUpRd+R6M/AkO77dBbgU6NjRbYBvzR+H7M+R+LmtEx+c7eZsVCvxSfkdOy9x2BvqfAFh2g+sGLtOD7sf5UNDn6Hgu0w/G+XiCwYJ4OM7/Cnn1xbAMnAaMUeYV4/SUZEsu1MWCuhgXsB7C+4tAkzl4kdcQWpFAbAOKXPSZ2cY2QV3ZHjTZcfW0Tb6B2FiWn5DiW89km6lbb/Me3hiF288JITLNC9pyu3G+uFpIvSXn7Muf72kJNf/jCkhjMu/u99o+8Tj07SCaAOlT/UVkqt3JzuYRz3CTwi9xpKJ1L+XE0LPcUUPf73kAOE2I3N2+ughLQiEPqzzP01nqcLtjn5+luwTlb4uybsX5UpSRO+pf56AMlXGPNWnOxLEPnr+WG5NBBD1B9d/C30TNn4HhIha3ym0rRvZOsiQFeyiRHj0VK6kbRMBdGZrSvQ82TRWmZQ+lnduaEbKrtDTXcGdPPrGS7uStlx4kMlvKPQ6eIlBuq7CdMRNEh595Cc8GWjxl3DPQ6I+i8cfEjJSFuD8EnUO3z9MCcMfkm6TUtLUutetFfq9OF/nQb6VLeMdDCFz8UtPL9C+y99bhMYqni3B8HfdW5Gt7Ksi0b1P8oj3cui30sbvwg5mD/jgP52jjTIHix0g7IANleTXKsCo6/cSpDndoZdop7fnZNnlpBGN3NAr9Qqq5HFJYq6VtRPbTclHTtjqNivW65ToE5asp2fwm0CCR45NgiJ/mui4//7U0oSPfECHnrJDIspJffKJ5LzDlpigPHaVog54bo9iRLdLjkRsrKFpQvFpXZF+ssPSIFbk6otw97MBZlgOoi3PlmSDG9AHxWzb0P3kL+fDrBHUcwmT8T/VxoEsljR4jdeanTVhE2QlgD/M6hg2ncn1xwbIQQKLD5M6iw5Qv4zz9b0gFsmKwINGYG3XT2IcL+Xm9YpXSsI07vBMm7pi2/Pa7LWksMXX20qVok7CMreP0lRytJLWRVXdBfLpahHZoNxl2LfRH2qm6AM0yTZRCszdyFWGmDHYV6VyKRh6dRU420pyPcbW2iTQE2CZdJnVfKMlzD8CXWkuhmQSVcoxw58WrTWB4rzDSvcIofQFAva7oX7wshf8XIROvbAfb9JQw3MhLNwzjVBw4UBv+bPZESGRUGteWIF0KKcGdSQC3VaS9j+Tws2vixP27su75LayEOjIJYH6pKcr9pdDEwS3dVK6WPYHwpK9KfQKZ/BVpZA2OXTEAekC/ParIHxFYBUXxfbz3tlFiHr/BLbRT6qVSKJJzp5G6jTgN1D5yr8oodyJAPMnfqubQr4O/znAMIrsKWdVNmGx3MH5uz/+S8e2ee21vUoo1xvduW4NxsE5q+TGOazFGVnliB3+SR5Z+oawUGgBuk5+7crdOLzy291tnhi6bQtyO08Uyso9VeN+PMx8ZehuarJC1W+LLI6x8D3ene/t3SkJ0KewLcCs3L7Lo5fYdH8PmHdCDwPsz+ZOWYcBXi33zMxOlUArJ4NZOXs69HcrkUxRDhDJPAqubSr9nWgrNIfxNgAEAtFu/YPhnJ+0AAAAASUVORK5CYII=) no-repeat center center; text-indent: -999px; - background-size:150px; + background-size: 150px; } .ssl_applay_info .paymethod{ width: 200px; @@ -8920,11 +9030,11 @@ select[disabled]{ font-size: 28px; } .ssl_order_check .lib-price-detailed, -.ssl_applay_info .lib-price-detailed{ +.ssl_applay_info .lib-price-detailed { margin: 0 auto; - margin-top: 15px; - width: 420px; - border-top: 1px solid #dedede; + margin-top: 15px; + width: 420px; + border-top: 1px solid #dedede; border-bottom: 1px solid #dedede; } @@ -8948,19 +9058,19 @@ select[disabled]{ font-size: 24px; } -.ssl_applay_info .lib-prompt span{ +.ssl_applay_info .lib-prompt span { display: block; - margin: 0 auto; - margin-top: 25px; - padding-left: 45px; - width: 200px; - height: 45px; - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAADMElEQVR4Xu1abU7bQBB9YxupUo3qcoHSE5QI+N1wg9yg9AQNJ6A9AekNwg1yA+jvtEo4QeEC1AgjVYq9g9bUNA0f3vFqqVE2v9/OvHnzdrO7XsKS/2jJ64cXwDtgyRXwU8DGAMkECTh+VxuD8rO08/u0FjcHSCYv1sHRm9oxlJ2kHaS1uAcAjRygCw/yeB9EfZPEDKQczjqmIujig2Llp0nsEsM8UFH2pYkQYgF08ZTHR0S0YUwQgCL1Md28GpqMWRvHn0G0b4KtMMw85SjbkYogFmDtezwA6JOEnMaqcPbWmQNuyfDX863MyJXVEJEApfWL1V+3qgMXBB4qxuhRQaL81LT4Kk65BuTR+mNxA0KPQbsEvKpwKrx8LXGBTIBx3A2Ijv6SkisudU4dftGRinkn3c6O68Y1c8CCANJkpqQkuMSSk5UDvABCu0k6a4r1DvBT4N+FWTot/RpgOtc0zna+SXKZYm05eQeYKl06YO6QwsAFh7MN6Q5Pks8Eu3hwkmy5dXyRA25FyKNdFeXD/138/LY5aMhJLIBJV54TxgvwnLrlgqvYAWvj+IAJossQF8Tvi0mM6fl2tifJJxJg8T9XkuipsE+6E3yqoiR5nAqgiegLCAb1CKi/sZUwt8QycEbgkdMrMUuOrRwuWgNaWYElKS+AVMDytrYI3yMsvrVpK9yUk8gBC4ch0dceqdCmeM2JipUJAYke4/QwZHv2Ni1KgrPlJHOA5f2bpDBTrBfAsineAaZWKy9DLNWW5DLF2nLyDjBV2jvAT4G7awCwbJ/H7z6QSIl4oAiPf4939EgqYHSZqV/tAm92gg4fSNzcB6zqdz4fJGtHk0dS89tbQa7D863LXQG+yXcBJFTExwSqfx43x0SR2ks3rwYm5JIfL/sBBwcm2ArD4BMOs67keYweK/obrJL9eSukizF2guSQIn4mBxyq8LIvLb6xAPNCII83AqD7ULdUwCmCYiQ9OpfHbhX2AkXlKe++nwKOEWXTJoVX8Ro5QGLNtmO9AG3vkGt+3gGuFW57fO+AtnfINT/vANcKtz2+d0DbO+San3eAa4XbHt87oO0dcs1v6R1wDSbCMF8UwqNRAAAAAElFTkSuQmCC) no-repeat; - text-align: center; + margin: 0 auto; + margin-top: 25px; + padding-left: 45px; + width: 200px; + height: 45px; + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAADMElEQVR4Xu1abU7bQBB9YxupUo3qcoHSE5QI+N1wg9yg9AQNJ6A9AekNwg1yA+jvtEo4QeEC1AgjVYq9g9bUNA0f3vFqqVE2v9/OvHnzdrO7XsKS/2jJ64cXwDtgyRXwU8DGAMkECTh+VxuD8rO08/u0FjcHSCYv1sHRm9oxlJ2kHaS1uAcAjRygCw/yeB9EfZPEDKQczjqmIujig2Llp0nsEsM8UFH2pYkQYgF08ZTHR0S0YUwQgCL1Md28GpqMWRvHn0G0b4KtMMw85SjbkYogFmDtezwA6JOEnMaqcPbWmQNuyfDX863MyJXVEJEApfWL1V+3qgMXBB4qxuhRQaL81LT4Kk65BuTR+mNxA0KPQbsEvKpwKrx8LXGBTIBx3A2Ijv6SkisudU4dftGRinkn3c6O68Y1c8CCANJkpqQkuMSSk5UDvABCu0k6a4r1DvBT4N+FWTot/RpgOtc0zna+SXKZYm05eQeYKl06YO6QwsAFh7MN6Q5Pks8Eu3hwkmy5dXyRA25FyKNdFeXD/138/LY5aMhJLIBJV54TxgvwnLrlgqvYAWvj+IAJossQF8Tvi0mM6fl2tifJJxJg8T9XkuipsE+6E3yqoiR5nAqgiegLCAb1CKi/sZUwt8QycEbgkdMrMUuOrRwuWgNaWYElKS+AVMDytrYI3yMsvrVpK9yUk8gBC4ch0dceqdCmeM2JipUJAYke4/QwZHv2Ni1KgrPlJHOA5f2bpDBTrBfAsineAaZWKy9DLNWW5DLF2nLyDjBV2jvAT4G7awCwbJ/H7z6QSIl4oAiPf4939EgqYHSZqV/tAm92gg4fSNzcB6zqdz4fJGtHk0dS89tbQa7D863LXQG+yXcBJFTExwSqfx43x0SR2ks3rwYm5JIfL/sBBwcm2ArD4BMOs67keYweK/obrJL9eSukizF2guSQIn4mBxyq8LIvLb6xAPNCII83AqD7ULdUwCmCYiQ9OpfHbhX2AkXlKe++nwKOEWXTJoVX8Ro5QGLNtmO9AG3vkGt+3gGuFW57fO+AtnfINT/vANcKtz2+d0DbO+San3eAa4XbHt87oO0dcs1v6R1wDSbCMF8UwqNRAAAAAElFTkSuQmCC) no-repeat; + text-align: center; line-height: 45px; background-size: 45px; font-size: 15px; - color: #777; + color: #777; font-weight: 500; background-position: 20px 3px; } @@ -8968,19 +9078,19 @@ select[disabled]{ .ssl_order_check{ padding:20px; } -.ssl_order_check .order_paly_success{ - font-size: 25px; - text-align: center; - padding: 15px 0 0; - color: #20a53a; +.ssl_order_check .order_paly_success { + font-size: 25px; + text-align: center; + padding: 15px 0 0; + color: #20a53a; } .lib-price-button button { padding: 8px 45px; margin:0 15px; } -.lib-price-button{ - margin: 25px 179px 45px; +.lib-price-button { + margin: 25px 179px 45px; } .ssl_order_check .lib-price-detailed{ @@ -9011,9 +9121,9 @@ select[disabled]{ padding-top: 10px; } -.lib-ssl-parsing{ - width: 510px; - margin: 50px 50px 50px 45px; +.lib-ssl-parsing { + width: 510px; + margin: 50px 50px 50px 45px; } .lib-ssl-parsing .parsing_tips .highlight{ color: #20a53a; @@ -9107,7 +9217,7 @@ select[disabled]{ position: fixed; left: 50%; top: 50%; - background: rgb(0 0 0 /.85); + background: rgb(0 0 0 /.85); z-index: 9999; height: 650px; width: 750px; @@ -9115,7 +9225,7 @@ select[disabled]{ margin-top: -325px; overflow: hidden; -webkit-box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.3); box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.3); border-radius: 4px; } @@ -9126,9 +9236,9 @@ select[disabled]{ position: absolute; z-index: 999; } -.preview_images_mask .preview_title{ - text-align: center; - width: 100%; +.preview_images_mask .preview_title { + text-align: center; + width: 100%; display: inline-block; font-size: 16px; background-color: rgb(0 0 0 /.45); @@ -9137,14 +9247,14 @@ select[disabled]{ } .preview_images_mask .preview_close, .preview_images_mask .preview_full, -.preview_images_mask .preview_small{ +.preview_images_mask .preview_small { position: absolute; - right: 0; - width: 45px; - height: 45px; - line-height: 47px; - font-size: 19px; - text-align: center; + right: 0; + width: 45px; + height: 45px; + line-height: 47px; + font-size: 19px; + text-align: center; color: #d6d6d6; } .preview_images_mask .preview_full, @@ -9159,11 +9269,11 @@ select[disabled]{ color: #fff; cursor: pointer; } -.preview_images_mask .preview_body{ +.preview_images_mask .preview_body { position: relative; height: 100%; - z-index: 99; - padding:45px 0; + z-index: 99; + padding: 45px 0; } .preview_images_mask:hover .preview_cut_view{ opacity:1; @@ -9171,17 +9281,17 @@ select[disabled]{ .preview_images_mask:hover .preview_toolbar{ opacity:1; } -.preview_images_mask .preview_body img{ +.preview_images_mask .preview_body img { position: absolute; display: none; transition: all 200ms; cursor: all-scroll; - display: inline-block; - opacity: 0; - webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; + display: inline-block; + opacity: 0; + webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } .preview_images_mask .preview_toolbar{ position: absolute; @@ -9245,12 +9355,12 @@ select[disabled]{ .preview_images_mask .preview_cut_view a:nth-child(2){ right: 3%; } -.preview_images_mask .preview_cut_view span{ +.preview_images_mask .preview_cut_view span { vertical-align: top; - height: 50px; - width: 50px; - text-align: center; - line-height: 50px; + height: 50px; + width: 50px; + text-align: center; + line-height: 50px; } /* end */ @@ -9268,7 +9378,7 @@ select[disabled]{ .nginx-add-format .bt-select-tips{ color: #999; } -.nginx-add-format .bt-select-list{ +.nginx-add-format .bt-select-list { display: none; position: absolute; z-index: 999; @@ -9278,7 +9388,7 @@ select[disabled]{ background: #fff; border: 1px solid #ccc; line-height: 30px; - width: 211px; + width: 211px; left: 10px; } .nginx-add-format .bt-select-list.active{ @@ -9311,32 +9421,32 @@ select[disabled]{ transform: rotate(-45deg); transition: all 300ms; } -#tab-nginx-logs-format tbody .nginx-one-format{ +#tab-nginx-logs-format tbody .nginx-one-format { display: inline-block; - margin-right: 10px; + margin-right: 10px; } -.nginx-add-site .bt_checkbox_groups{ +.nginx-add-site .bt_checkbox_groups { display: inline-block; - height: 16px; - width: 16px; - border-radius: 1px; - cursor: pointer; - border: 1px solid #c2c2c2; - position: relative; - text-align: center; - line-height: 20px; - margin: 0; - vertical-align: middle; - margin-right: 10px; + height: 16px; + width: 16px; + border-radius: 1px; + cursor: pointer; + border: 1px solid #c2c2c2; + position: relative; + text-align: center; + line-height: 20px; + margin: 0; + vertical-align: middle; + margin-right: 10px; } -.nginx-add-site .format-site-list{ +.nginx-add-site .format-site-list { width: 250px; margin-left: 45px; - display: inline-block; - overflow: auto; - max-height: 200px; - border: 1px solid #ececec; - border-radius: 2px; + display: inline-block; + overflow: auto; + max-height: 200px; + border: 1px solid #ececec; + border-radius: 2px; } .nginx-add-site .bt_checkbox_groups.active { border: none; @@ -9401,13 +9511,13 @@ select[disabled]{ } .redirect_check label span, .redirect_checkbox label span, -.redirect_tourl label span{ - max-width: 55px; - display: inline-block; - text-overflow: ellipsis; - white-space: pre; - overflow: hidden; - vertical-align: middle; +.redirect_tourl label span { + max-width: 55px; + display: inline-block; + text-overflow: ellipsis; + white-space: pre; + overflow: hidden; + vertical-align: middle; } .redirect_check label span{ max-width: 140px; @@ -9433,65 +9543,65 @@ select[disabled]{ margin-right: 5px; } .site_ssl_check i.form-checkbox { - vertical-align: inherit; + vertical-align: inherit; } /*首页概览网卡选择*/ -select[name="network-io"]{ - font-size: 13px; - height: 24px; - line-height: 24px; - padding: 2px; - border-radius: 0; - border-color: #ececec; +select[name="network-io"] { + font-size: 13px; + height: 24px; + line-height: 24px; + padding: 2px; + border-radius: 0; + border-color: #ececec; } /*批量操作*/ -.batch_title{ - height: 30px; - display: inline-block; - width: 100%; +.batch_title { + height: 30px; + display: inline-block; + width: 100%; margin-top: 25px; text-align: center; } -.batch_title .batch_text{ - vertical-align: top; - height: 30px; - line-height: 30px; - display: inline-block; - margin-left: 8px; - font-size: 18px; +.batch_title .batch_text { + vertical-align: top; + height: 30px; + line-height: 30px; + display: inline-block; + margin-left: 8px; + font-size: 18px; } -.batch_title .batch_icon{ - background-image: url('/static/layer/skin/default/icon.png'); - background-position: -30px 0; - height: 30px; - width: 30px; - display: inline-block +.batch_title .batch_icon { + background-image: url('/static/layer/skin/default/icon.png'); + background-position: -30px 0; + height: 30px; + width: 30px; + display: inline-block } -.bt_table{ - position: relative; +.bt_table { + position: relative; } -.bt_table .divtable{ - overflow: auto; +.bt_table .divtable { + overflow: auto; } .bt_table .text-overflow{ display: inline-block; vertical-align: middle; word-wrap:break-word; } -#recycle_table .divtable{ - max-height: 480px; +#recycle_table .divtable { + max-height: 480px; } -.re-con-con .text-overflow span{ +.re-con-con .text-overflow span { vertical-align: middle; margin-left: 5px; display: inline-block; text-overflow: ellipsis; - overflow: hidden; + overflow: hidden; white-space: pre; } .recycle_view .layui-layer-content{ @@ -9508,16 +9618,16 @@ select[name="network-io"]{ border: 1px solid #ddd; } -.bt_table .file_icon{ - width: 26px; - height: 26px; +.bt_table .file_icon { + width: 26px; + height: 26px; display: inline-block; - background-repeat: no-repeat; - background-size: cover; - background-position: center; - image-rendering: -webkit-optimize-contrast; - background-image: url(../img/file_icon/file.png); - cursor: pointer; + background-repeat: no-repeat; + background-size: cover; + background-position: center; + image-rendering: -webkit-optimize-contrast; + background-image: url(../img/file_icon/file.png); + cursor: pointer; vertical-align: middle; margin-left: -5px; } @@ -9527,310 +9637,322 @@ select[name="network-io"]{ .bt_table .file_images{ background-image: url(../img/file_icon/png.png); } -.bt_table .table-mask{ - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; +.bt_table .table-mask { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; } .bt_table tr th>span, -.bt_table tr td>span{ - display: inline-block; - width: 100%; - position: relative; +.bt_table tr td>span { + display: inline-block; + width: 100%; + position: relative; } .bt_table .cust—checkbox, -.bt-form .form-checkbox{ - display: inline-block; - width: 16px; - height: 16px; - border: 1px solid #ccc; - background-color: #fff; - vertical-align: top; - border-radius: 1px; - position: relative; - border-radius: 2px; +.bt-form .form-checkbox { + display: inline-block; + width: 16px; + height: 16px; + border: 1px solid #ccc; + background-color: #fff; + vertical-align: top; + border-radius: 1px; + position: relative; + border-radius: 2px; } + +.bt-form .form-checkbox { + vertical-align: middle +} + .bt_table .cust—checkbox.active, .bt_table .cust—checkbox.selected, .bt-form .form-checkbox.active, -.bt-form .form-checkbox.selected{ - background-color: #20a53a; - border-color: #20a53a; +.bt-form .form-checkbox.selected { + background-color: #20a53a; + border-color: #20a53a; } .bt_table .cust—checkbox.active::after, -.bt-form .form-checkbox.active::after{ - content: ''; - position: absolute; - display: block; - left: 50%; - top: 50%; - margin-left:-2.5px; - margin-top:-6px; - width: 5px; - height: 10px; - border: solid #fff; - border-width: 0 2px 2px 0; - transform: rotate(45deg); +.bt-form .form-checkbox.active::after { + content: ''; + position: absolute; + display: block; + left: 50%; + top: 50%; + margin-left: -2.5px; + margin-top: -6px; + width: 5px; + height: 10px; + border: solid #fff; + border-width: 0 2px 2px 0; + transform: rotate(45deg); } .bt_table .cust—checkbox.selected::after, -.bt-form .form-checkbox.selected::after{ - content: ''; - position: absolute; - display: block; - left: 50%; - top: 50%; - margin-left:-5px; - margin-top:-1px; - height: 2px; - width: 10px; - background-color: #fff; +.bt-form .form-checkbox.selected::after { + content: ''; + position: absolute; + display: block; + left: 50%; + top: 50%; + margin-left: -5px; + margin-top: -1px; + height: 2px; + width: 10px; + background-color: #fff; } .bt_table .cust—checkbox+input, -.bt-form .form-checkbox+input{ - display: none; +.bt-form .form-checkbox+input { + display: none; } .bt_table .cursor-pointer, -.bt-form .cursor-pointer{ - cursor: pointer; +.bt-form .cursor-pointer { + cursor: pointer; } -.bt_table thead .sort-active span{ - font-weight: 500; +.bt_table thead .sort-active span { + font-weight: 500; } -.vertical_middle{ - vertical-align: middle; +.vertical_middle { + vertical-align: middle; } -.bt-form .form-checkbox-label span{ - font-weight: 400; +.bt-form .form-checkbox-label span { + font-weight: 400; } /* .bt_table thead .sort-active .glyphicon{ color: #20a53a; } */ -.bt_table thead .glyphicon{ - color: #bbb; - transform: scale(.9); - top: 2px; - left: -1px; +.bt_table thead .glyphicon { + color: #bbb; + transform: scale(.9); + top: 2px; + left: -1px; } -.bt_table label{ - margin: 0; +.bt_table label { + margin: 0; } -.bt_table .table-input{ - border: 1px solid transparent; - border-radius:2px; - padding-left: 3px; - width: 100%; - outline: none; +.bt_table .table-input { + border: 1px solid transparent; + border-radius: 2px; + padding-left: 3px; + width: 100%; + outline: none; } -.bt_table table tr:hover .table-input{ - background-color:#f5f5f5; +.bt_table table tr:hover .table-input { + background-color: #f5f5f5; } .bt_table .table-input:hover, -.bt_table .table-input:focus{ - border:1px solid #ccc; - background-color: #fff !important; +.bt_table .table-input:focus { + border: 1px solid #ccc; + background-color: #fff !important; } -.bt_table .table-input:focus{ - border-color: #20a53a; +.bt_table .table-input:focus { + border-color: #20a53a; } -.bt_table .bt_batch{ - height: 28px; +.bt_table .bt_batch { + height: 28px; } -.bt_table .bt_batch label{ - padding: 5px 10px; - height: 28px; +.bt_table .bt_batch label { + padding: 5px 10px; + height: 28px; } -.tootls_group{ - min-height:30px; +.tootls_group { + min-height: 30px; line-height: 30px; } .tootls_group::after { - content: ''; - display: block; - width: 0; - clear: both; + content: ''; + display: block; + width: 0; + clear: both; } -.tootls_group .bt_search{ - display: inline-block; - height: 30px; - line-height: 30px; - position: relative; +.tootls_group .bt_search { + display: inline-block; + height: 30px; + line-height: 30px; + position: relative; } -.tootls_group .bt_search .search_input{ - height: 30px; - line-height: 30px; - border-radius: 2px; - border:1px solid #ccc; - outline: none; - padding-left: 8px; - vertical-align: top; - width:240px; +.tootls_group .bt_search .search_input { + height: 30px; + line-height: 30px; + border-radius: 2px; + border: 1px solid #ccc; + outline: none; + padding-left: 8px; + vertical-align: top; + width: 240px; } -.tootls_group .bt_search .search_input:focus{ - border-color: #20a53a; +.tootls_group .bt_search .search_input:focus { + border-color: #20a53a; } .tootls_group .bt_search .search_input:focus + span.glyphicon-search{ color: #20a53a; } -.tootls_group .bt_search .glyphicon-remove-sign{ - position: absolute; - top: 8px; - right: 30px; - font-size: 15px; - cursor: pointer; +.tootls_group .bt_search .glyphicon-remove-sign { + position: absolute; + top: 8px; + right: 30px; + font-size: 15px; + cursor: pointer; } -.tootls_group .bt_search .glyphicon-search{ - height: 28px; - line-height: 28px; - padding: 0 10px; - color: #888; - position: absolute; - right: 0; - font-size: 14px; - cursor: pointer; +.tootls_group .bt_search .glyphicon-search { + height: 28px; + line-height: 28px; + padding: 0 10px; + color: #888; + position: absolute; + right: 0; + font-size: 14px; + cursor: pointer; } -.tootls_group .page .page_jump_group .page_jump_btn:hover{ - background-color: #10952a; - border-color: #398439; +.tootls_group .page .page_jump_group .page_jump_btn:hover { + background-color: #10952a; + border-color: #398439; } -.tootls_group .bt_search .search-btn span{ - font-size: 15px; - vertical-align: middle; - position: relative; - top: -1px; +.tootls_group .bt_search .search-btn span { + font-size: 15px; + vertical-align: middle; + position: relative; + top: -1px; } .tootls_group .page { - padding: 0; - height: 30px; + padding: 0; + height: 30px; } .tootls_group .page .page_select_number, -.tootls_group .page .page_jump_group{ - height: 28px; - line-height: 28px; - margin-left: 8px; - vertical-align: top; - float: left; +.tootls_group .page .page_jump_group { + height: 28px; + line-height: 28px; + margin-left: 8px; + vertical-align: top; + float: left; } -.tootls_group .page .page_jump_group span{ - border: none; - padding:0 8px; +.tootls_group .page .page_jump_group span { + border: none; + padding: 0 8px; } -.tootls_group .page .page_select_number{ - border: 1px solid #ececec; - border-radius: 0; - padding: 0 5px; - border-left: 0; - margin-left: 0; +.tootls_group .page .page_select_number { + border: 1px solid #ececec; + border-radius: 0; + padding: 0 5px; + border-left: 0; + margin-left: 0; } -.tootls_group .page .page_jump_group input{ - height: 28px; - line-height: 28px; - width: 30px; - max-width: 50px; - vertical-align: top; - border: 1px solid #ececec; - outline: none; - text-align: center; - float: left; +.tootls_group .page .page_jump_group input { + height: 28px; + line-height: 28px; + width: 30px; + max-width: 50px; + vertical-align: top; + border: 1px solid #ececec; + outline: none; + text-align: center; + float: left; } -.tootls_group .page .page_jump_group input:focus{ - border-color: #20a53a; +.tootls_group .page .page_jump_group input:focus { + border-color: #20a53a; } -.tootls_group .page .page_jump_group .page-jump-title{ - height: 28px; - line-height: 28px; - color: #555; - border: none; - padding:0 5px +.tootls_group .page .page_jump_group .page-jump-title { + height: 28px; + line-height: 28px; + color: #555; + border: none; + padding: 0 5px } -.tootls_group .page .page_jump_group .page_jump_btn{ - height: 28px; - line-height: 26px; - border-radius:0; - outline: none; - margin-left: 8px; - border-radius: 2px; - color: #555; - background-color: #fff; - border: 1px solid #ccc; +.tootls_group .page .page_jump_group .page_jump_btn { + height: 28px; + line-height: 26px; + border-radius: 0; + outline: none; + margin-left: 8px; + border-radius: 2px; + color: #555; + background-color: #fff; + border: 1px solid #ccc; } -.tootls_group .page .page_jump_group .page_jump_btn:hover{ - color: #fff; - background-color: #20a53a; - border-color: #20a53a; +.tootls_group .page .page_jump_group .page_jump_btn:hover { + color: #fff; + background-color: #20a53a; + border-color: #20a53a; } -.bt_table .bt_success{ - color: #20a53a; +.bt_table .bt_success { + color: #20a53a; } -.bt_table .bt_warning{ - color: #f0ad4e; +.bt_table .bt_warning { + color: #f0ad4e; } -.bt_table .bt_danger{ - color: #d9534f; +.bt_table .bt_danger { + color: #d9534f; } -.bt_table .bt_selects{ - display: inline-block; +.bt_table .bt_selects { + display: inline-block; } .bt_table input::-webkit-outer-spin-button, .bt_table input::-webkit-inner-spin-button { - -webkit-appearance: none; + -webkit-appearance: none; } -.bt_tableinput[type="number"]{ - -moz-appearance: textfield; +.bt_tableinput[type="number"] { + -moz-appearance: textfield; } .bt_table_select_group, -.bt_select_updown{ - display: inline-block; - border: 1px solid #ccc; - border-radius: 2px; - height: 28px; - line-height: 28px; - cursor: pointer; - color:#666; - position: relative; +.bt_select_updown { + display: inline-block; + border: 1px solid #ccc; + border-radius: 2px; + height: 28px; + line-height: 28px; + cursor: pointer; + color: #666; + position: relative; +} + +.bt_select_updown.bt-disabled { + background-color: rgb(246, 246, 246); + color: rgb(102, 102, 102); } .bt_table_select_group { - vertical-align: top; + vertical-align: top; } -.bt_select_updown{ - height: 30px; +.bt_select_updown { + height: 30px; line-height: 30px; - vertical-align: middle; + vertical-align: middle; } -.bt_select_updown .bt_select_value{ + +.bt_select_updown .bt_select_value { height: 28px; line-height: 28px; - padding:0 10px; + padding: 0 10px; width: 100%; position: relative; overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; + white-space: nowrap; + text-overflow: ellipsis; } -.bt_select_updown .bt_select_list{ + +.bt_select_updown .bt_select_list { display: none; transition: all 500ms; - background: #fff; - position: absolute; + background: #fff; + position: absolute; top: 30px; - display: none; - box-shadow: 0 1px 5px rgba(0, 0, 0, .5); + display: none; + box-shadow: 0 1px 5px rgba(0, 0, 0, .5); border-radius: 1px; - /* width: 100%; */ + width: 100%; z-index: 999; background-color: #fff; max-height: 200px; @@ -9840,19 +9962,19 @@ select[name="network-io"]{ transform: rotate(180deg); transition: all 500ms; } -.bt_select_updown .bt_select_list .item{ +.bt_select_updown .bt_select_list .item { height: 28px; line-height: 28px; color: #333; - padding:0 10px; + padding: 0 10px; overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; + white-space: nowrap; + text-overflow: ellipsis; } -.bt_select_updown .bt_select_list .item.disabled{ +.bt_select_updown .bt_select_list .item.disabled { opacity: .65; - cursor: no-drop; - box-shadow: none; + cursor: no-drop; + box-shadow: none; } .bt_select_updown .bt_select_list .item:hover{ background-color: #f2f2f2; @@ -9864,13 +9986,13 @@ select[name="network-io"]{ .bt_select_updown .bt_select_list.active{ display: inline-block; } -.bt_select_value{ - padding:0 6px; - height: 26px; - line-height: 26px; - color: #777; - display: inline-block; - vertical-align: top; +.bt_select_value { + padding: 0 6px; + height: 26px; + line-height: 26px; + color: #777; + display: inline-block; + vertical-align: top; } .bt_select_updown .bt_select_value .bt_select_content{ margin-right: 15px; @@ -9888,46 +10010,46 @@ select[name="network-io"]{ top:50%; margin-top: -6px; } -.bt_select_value .glyphicon{ - position: relative; - top: 2px; +.bt_select_value .glyphicon { + position: relative; + top: 2px; } .bt_select_value em{ font-style: inherit; margin: 0 5px; } .bt_table_select_group.bt_disabled, -.bt_table_select_group.bt_disabled{ - opacity: .65; - cursor: no-drop; - box-shadow: none; +.bt_table_select_group.bt_disabled { + opacity: .65; + cursor: no-drop; + box-shadow: none; } -.bt_table_select_group .bt_selects{ - display: none; - background-color: #fff; - border:1px solid #ccc; - position: absolute; - left: -1px; - right: -1px; - top: 100%; - box-shadow: 0 2px 4px rgba(0,0,0,.12); - z-index: 9999; +.bt_table_select_group .bt_selects { + display: none; + background-color: #fff; + border: 1px solid #ccc; + position: absolute; + left: -1px; + right: -1px; + top: 100%; + box-shadow: 0 2px 4px rgba(0, 0, 0, .12); + z-index: 9999; } -.bt_table_select_group .bt_selects.top{ - box-shadow: 0 -2px 4px rgba(0,0,0,.12); - top: auto; - bottom:100%; +.bt_table_select_group .bt_selects.top { + box-shadow: 0 -2px 4px rgba(0, 0, 0, .12); + top: auto; + bottom: 100%; } -.bt_table_select_group.active .bt_selects{ - display: inline-block; +.bt_table_select_group.active .bt_selects { + display: inline-block; } -.bt_table_select_group .bt_selects .item{ - width: 100%; - height: 28px; - line-height: 28px; - padding:0 8px; +.bt_table_select_group .bt_selects .item { + width: 100%; + height: 28px; + line-height: 28px; + padding: 0 8px; } .bt_table_select_group .bt_selects .item.no_drop{ cursor: no-drop; @@ -9936,36 +10058,36 @@ select[name="network-io"]{ background-color: #ececec !important; } .bt_table_select_group .bt_selects .item.active, -.bt_table_select_group .bt_selects .item:hover{ - background-color: #20a53a; - color: #fff; +.bt_table_select_group .bt_selects .item:hover { + background-color: #20a53a; + color: #fff; } .bt_batch .bt-disabled, .bt_batch .bt-disabled:hover, -.bt_batch .bt-disabled:active{ +.bt_batch .bt-disabled:active { opacity: .65; - box-shadow: none; - color: #777; - background-color: #ffffff; - cursor: no-drop; + box-shadow: none; + color: #777; + background-color: #ffffff; + cursor: no-drop; } .bt_batch .bt-disabled.btn-default:hover{ border-color: #ccc; } .bt_batch .btn-default, -.bt_batch .btn-success{ - vertical-align: top; - padding: 4px 10px; +.bt_batch .btn-success { + vertical-align: top; + padding: 4px 10px; margin-left: 5px; } -.not-select{ - moz-user-select: -moz-none; - -moz-user-select: none; - -o-user-select:none; - -khtml-user-select:none; - -webkit-user-select:none; - -ms-user-select:none; - user-select:none; +.not-select { + moz-user-select: -moz-none; + -moz-user-select: none; + -o-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; } .inlineBlock{ @@ -9977,9 +10099,9 @@ select[name="network-io"]{ /*.custom_layer .layui-layer-content{*/ /* overflow: initial !important;*/ /*}*/ -.offsetY7{ +.offsetY7 { position: relative; - top: 7px; + top: 7px; } .blur_checked{ @@ -9991,16 +10113,16 @@ select[name="network-io"]{ /*.custom_layer .layui-layer-content {*/ /* overflow: initial !important;*/ /*}*/ -.custom_layer .layui-layer-content .tab-body .tab-con{ - overflow: initial; +.custom_layer .layui-layer-content .tab-body .tab-con { + overflow: initial; } -.limit-site-height{ +.limit-site-height { max-height: 402px; - overflow-y: auto; + overflow-y: auto; } -.block.redirect_check label{ +.block.redirect_check label { vertical-align: middle; - margin-left: 5px; + margin-left: 5px; color: #444; font-weight: normal; margin-top: 10px; @@ -10008,9 +10130,9 @@ select[name="network-io"]{ .block.redirect_check input{ vertical-align: middle; } -.block.redirect_tourl{ +.block.redirect_tourl { height: 24px; - line-height: 24px; + line-height: 24px; } .block.redirect_tourl label{ font-weight: normal; @@ -10288,7 +10410,7 @@ select[name="network-io"]{ background-color:#FF6232; } .recommend-tips { -background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNNDggMGwzNCAzNHY0OEwwIDBoNDh6IiBmaWxsPSIjRkY2MjMyIi8+PHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoNDUgNTIuMDQyIDI5LjA0MikiIGZvbnQtZmFtaWx5PSJQaW5nRmFuZ1NDLU1lZGl1bSwgUGluZ0ZhbmcgU0MiIGZvbnQtc2l6ZT0iMjAiIGZvbnQtd2VpZ2h0PSI0MDAiIGZpbGw9IiNGRkYiPjx0c3BhbiB4PSIzMi4wNDIiIHk9IjM2LjA0MiI+5o6o6I2QPC90c3Bhbj48L3RleHQ+PC9nPjwvc3ZnPg==) 50% no-repeat; + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNNDggMGwzNCAzNHY0OEwwIDBoNDh6IiBmaWxsPSIjRkY2MjMyIi8+PHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoNDUgNTIuMDQyIDI5LjA0MikiIGZvbnQtZmFtaWx5PSJQaW5nRmFuZ1NDLU1lZGl1bSwgUGluZ0ZhbmcgU0MiIGZvbnQtc2l6ZT0iMjAiIGZvbnQtd2VpZ2h0PSI0MDAiIGZpbGw9IiNGRkYiPjx0c3BhbiB4PSIzMi4wNDIiIHk9IjM2LjA0MiI+5o6o6I2QPC90c3Bhbj48L3RleHQ+PC9nPjwvc3ZnPg==) 50% no-repeat; background-size: 70px; position: absolute; right: 0; @@ -10296,25 +10418,25 @@ background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHh height: 70px; width: 70px; } -.pro-font-icon{ - background-image:url('../img/pro.svg'); +.pro-font-icon { + background-image: url('../img/pro.svg'); background-repeat: no-repeat; background-size: 50px; - background-position: center; + background-position: center; display: inline-block; width: 66px; height: 19px; } -.ltd-font-icon{ - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEMAAAARCAYAAAHxiyEZAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAQ6ADAAQAAAABAAAAEQAAAAAQuP9PAAAIzklEQVRYCd1XfXBVxRU/u/fel5Avklo+glXGVinWjyIFBGSoKNa8xGSkEL6kYEgC49RhOtYpFCS5eRE/mLbOWOy05oWAyqjQqUwgeXz4UTuOjEhtWoYCRcq0ED6CIknM17v37vZ37uO+PDCZ6mj/6c68t2fPnnP27NlzfruXCC1aHX67IVJ4C9MDNZOZWuvprqv/BlLUVRXoysguUV9b8m3lxI8ITPuKW+3SkE8M9he1w5tYe7B5Uyu9BJMnWAB+fYJ1D0N6Ci8Hxe2DKSb5Ilpd+JwmPR6ao+GYC20T2qNYAhYumtKarJQzT0vK14qWSys0tnxt49FoTcHd2hNPjpTmndBLNPj7uNY0rLImtjzg1VWHu2A8wyC5UMG4JvWCkGJliNI3LbZfa6urLbqRHO/vvhHerCnFFFfpfbyHwMjn7XnTp4UpirSrm0Ihc2I87r7P22mw78+VppvpxL0dFZHYeDYYtYtulkQjFakfSiF2D9PG3mJ7Rzdo8bZWxlDI5MPAqSAeZfb2i0se29mKeN3mG6gO/5p7T3srscWrsfW8c9o9wzyzoia2IFpVoBBQwn5nMZMbeKWSJGfJQp+hxX4oFwuSLxCpzVqoduhIbdtw7lLbFCm+KaC/aJ8ILJ+CIacLV70HA83YQrEhxd2e1i8jG4ebpry1rKr54JXG6+zCqFBq/rirr82bsPx5J3WeD0tI4xZhGA6nBM8hA35jmcY6R3nDyROPaK2KwR46NDc7w+StozAyyPUOkBC/AP0oK3lKv4GYb4BTD3uu+itYya1Hq8KNIZlegVSpwILl7ERD7X03KE92CXLGCkr7wFW9bMZvkD+LAx7JAylCSpo9pzxPT4LFl4QWeXMf2dYjKyK7tnF6ITQnsejihCoOzaRJcOphU8p8yCSd4HkYLYnrXn+XgXzZ2p3Hyu3G00vt2Jue7q0P+MpT+aHsr3+LxyiaaT9a84czOi6KsMkXhRLfxMJ+kgTyyb7eDv8AUToStQuQDF9t2/qr0iFskaPXYBePDaxv3Vpq+DnCoOa6ijGLEkVIT8H/7yN7N1fYsQcDhf9VL5IOCGoTkhZqj14XJHZgwULAwu1IlD8BWjKvdODA75ZZLa3//gQ59SrKrjx1vj5y33eloG7Xcf+RiijR6oIY6rUAYDFNu/QMNjoRUTgqEYHdMHAmZJnjyaMGppEjE8ikiXBgA2q1nUEzdRFAb01L68n3RkhreEiYNQzFW58qZQCh+pqiGamyTHOVJHgiy+9dMYGBdKS0MnNys2+Dw8QZnO843iqAwTVM+z8PoKH1ZJ/W6o/o/Ra1S66XQm7Oyc264zypqR4ZHYhEXkf3py+ygFZec0Iy8e+DixL7/JHWTnpaho/457Rz6Dx5d7W3f/qunxNc45cUT6C/zqelOYOU+xay+RVGuUvzya6uqrAM2HLSU95eZgZh51IWprE2OA7kVR3yahnLJNcR4gmujpBhNrmON9kvQS5HFkJ5zuPeb9p9FR6eHMiBQIR7U6bnBQ7w2DB1LUrQYJobciAtQfG/eBSLX4DdcXBsQ5/j/HNppPllPxI8jXO7HwphaC2D9+tI6dVX4gTLcdtYVbjAE/pOkB8hUiekoi4t9ccWpbfEVd+9yKlrJYk4bp/1wP4EBkk6b5nyoOO4XIUZWotDKIQPQZ9NOoEBHAE+KJoEJ1aU27E9zPuqW31V+AGVZr2Oq6q7fGVjJ1fSZ9bgi+m/PmM+o/XlGMlIsIfIzjUI0U+U0ly2BLw4JyXdpbRu4jEu8yKl6E2EewSPDcMoADw/AyPryiOxLcz7Io3XZPnPq7vlyaK83j5cgkOs12SPNBT1lSDhti+yYx1XrsuB7KJMmZnTJTo7uwqlDr3Bb45Uubp1JSNMzxujlKfL7V3viJfscE6vogPY4A0siFxbjw654+cMHnQaKN7fkDIrsXlTkR6FC7EL993PeBaBO5YuacJAjvVrE21+YtZVTm/PNk1ijDCJb3cCgO5A4RzPEObshfaOj1LlU2lOIeW6LaZljsGlnAZYOhg8KFPlmPYxUuvrLMus4EcYgHo8uWI6VlujBdUBRY5qMo6kk3ck8NnsJWMsXqo+pLARH7ssedh03HyXZC5YM5gfNJ43SF1UlnVGO+pGRCHGc8zvJeI7cj+PU5sPAkpvwobTnHjfuhyZXTDX3hbfaBfNZMhjxCHK7e3W535eVxVeCbkevs3Kq2Mx1O1vEepcoMv8VJtX0rhA+DGRDnTzs43n+bbiQPiyHv0e/bM43GFwdrX/yiSXesDEmq2Vkdg3zAp75344W4WPhBpfif/6cJuZcha57ltJXgqBucnUF/8LWD7k8hQyphoOR1LEkiT4/JHBgfU/QNp1VyvIBh5z06E+69arkNV0zeOdZ0bVzrBtNzFDlPOd7B93HOps22iH5yDgxwJ+ah+NhO/AM+DBNJGe9AdWs5Dle2QaLWJZER/R6emzDyGr98HZ4/jCCFXW7JqHh9S7QNwPWMa/R3gTphw5BCcSnGo+cGMJNvg0C6U25vEceP7CrMO6gwUiVXcwGk/A2S2tp+bz7zjtn5kqN3fuNg+1+7SnqCqVH9C4pDPwHGlG2pfxZ1PAJy1GI3hxHZdT8Aw+rqhtPuzcDOBrIYseQ3jm8MUL3k1DSKxmPf728FuZvQlZTrc32IXjPKXeQaY8hKtxJjY7G+l2PQuB/pCE3pt4jFGXIeW0Mru5xTfwJf4khV4pi1wObqnmEOj1GK8f6NoRwuour2n031eBDsAzq113ToV/c5ZWNzXigefgfja0ECWGEiuyMjLbOto79yBYxULItYvsZh+AkTUDtzq7YBWi+1NDmPd6yvkzSxnS+p6n3d0IyC8r7V2XAevAVvq5qEvUrk48+PrZA1OC3kcK3wNMmQpMueztNrBCP9eQRqES3hB8eKxiGzyDYHyMvaCs9QUEIIRNj9WWMatybdNhfHMsxg35vDT0gkGDwUaanw2nnb5ADwBwRvMYYPmvUV+jLYUrYn3++P/s7z9Qrvg7vnEC6QAAAABJRU5ErkJggg==); +.ltd-font-icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEMAAAARCAYAAAHxiyEZAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAQ6ADAAQAAAABAAAAEQAAAAAQuP9PAAAIzklEQVRYCd1XfXBVxRU/u/fel5Avklo+glXGVinWjyIFBGSoKNa8xGSkEL6kYEgC49RhOtYpFCS5eRE/mLbOWOy05oWAyqjQqUwgeXz4UTuOjEhtWoYCRcq0ED6CIknM17v37vZ37uO+PDCZ6mj/6c68t2fPnnP27NlzfruXCC1aHX67IVJ4C9MDNZOZWuvprqv/BlLUVRXoysguUV9b8m3lxI8ITPuKW+3SkE8M9he1w5tYe7B5Uyu9BJMnWAB+fYJ1D0N6Ci8Hxe2DKSb5Ilpd+JwmPR6ao+GYC20T2qNYAhYumtKarJQzT0vK14qWSys0tnxt49FoTcHd2hNPjpTmndBLNPj7uNY0rLImtjzg1VWHu2A8wyC5UMG4JvWCkGJliNI3LbZfa6urLbqRHO/vvhHerCnFFFfpfbyHwMjn7XnTp4UpirSrm0Ihc2I87r7P22mw78+VppvpxL0dFZHYeDYYtYtulkQjFakfSiF2D9PG3mJ7Rzdo8bZWxlDI5MPAqSAeZfb2i0se29mKeN3mG6gO/5p7T3srscWrsfW8c9o9wzyzoia2IFpVoBBQwn5nMZMbeKWSJGfJQp+hxX4oFwuSLxCpzVqoduhIbdtw7lLbFCm+KaC/aJ8ILJ+CIacLV70HA83YQrEhxd2e1i8jG4ebpry1rKr54JXG6+zCqFBq/rirr82bsPx5J3WeD0tI4xZhGA6nBM8hA35jmcY6R3nDyROPaK2KwR46NDc7w+StozAyyPUOkBC/AP0oK3lKv4GYb4BTD3uu+itYya1Hq8KNIZlegVSpwILl7ERD7X03KE92CXLGCkr7wFW9bMZvkD+LAx7JAylCSpo9pzxPT4LFl4QWeXMf2dYjKyK7tnF6ITQnsejihCoOzaRJcOphU8p8yCSd4HkYLYnrXn+XgXzZ2p3Hyu3G00vt2Jue7q0P+MpT+aHsr3+LxyiaaT9a84czOi6KsMkXhRLfxMJ+kgTyyb7eDv8AUToStQuQDF9t2/qr0iFskaPXYBePDaxv3Vpq+DnCoOa6ijGLEkVIT8H/7yN7N1fYsQcDhf9VL5IOCGoTkhZqj14XJHZgwULAwu1IlD8BWjKvdODA75ZZLa3//gQ59SrKrjx1vj5y33eloG7Xcf+RiijR6oIY6rUAYDFNu/QMNjoRUTgqEYHdMHAmZJnjyaMGppEjE8ikiXBgA2q1nUEzdRFAb01L68n3RkhreEiYNQzFW58qZQCh+pqiGamyTHOVJHgiy+9dMYGBdKS0MnNys2+Dw8QZnO843iqAwTVM+z8PoKH1ZJ/W6o/o/Ra1S66XQm7Oyc264zypqR4ZHYhEXkf3py+ygFZec0Iy8e+DixL7/JHWTnpaho/457Rz6Dx5d7W3f/qunxNc45cUT6C/zqelOYOU+xay+RVGuUvzya6uqrAM2HLSU95eZgZh51IWprE2OA7kVR3yahnLJNcR4gmujpBhNrmON9kvQS5HFkJ5zuPeb9p9FR6eHMiBQIR7U6bnBQ7w2DB1LUrQYJobciAtQfG/eBSLX4DdcXBsQ5/j/HNppPllPxI8jXO7HwphaC2D9+tI6dVX4gTLcdtYVbjAE/pOkB8hUiekoi4t9ccWpbfEVd+9yKlrJYk4bp/1wP4EBkk6b5nyoOO4XIUZWotDKIQPQZ9NOoEBHAE+KJoEJ1aU27E9zPuqW31V+AGVZr2Oq6q7fGVjJ1fSZ9bgi+m/PmM+o/XlGMlIsIfIzjUI0U+U0ly2BLw4JyXdpbRu4jEu8yKl6E2EewSPDcMoADw/AyPryiOxLcz7Io3XZPnPq7vlyaK83j5cgkOs12SPNBT1lSDhti+yYx1XrsuB7KJMmZnTJTo7uwqlDr3Bb45Uubp1JSNMzxujlKfL7V3viJfscE6vogPY4A0siFxbjw654+cMHnQaKN7fkDIrsXlTkR6FC7EL993PeBaBO5YuacJAjvVrE21+YtZVTm/PNk1ijDCJb3cCgO5A4RzPEObshfaOj1LlU2lOIeW6LaZljsGlnAZYOhg8KFPlmPYxUuvrLMus4EcYgHo8uWI6VlujBdUBRY5qMo6kk3ck8NnsJWMsXqo+pLARH7ssedh03HyXZC5YM5gfNJ43SF1UlnVGO+pGRCHGc8zvJeI7cj+PU5sPAkpvwobTnHjfuhyZXTDX3hbfaBfNZMhjxCHK7e3W535eVxVeCbkevs3Kq2Mx1O1vEepcoMv8VJtX0rhA+DGRDnTzs43n+bbiQPiyHv0e/bM43GFwdrX/yiSXesDEmq2Vkdg3zAp75344W4WPhBpfif/6cJuZcha57ltJXgqBucnUF/8LWD7k8hQyphoOR1LEkiT4/JHBgfU/QNp1VyvIBh5z06E+69arkNV0zeOdZ0bVzrBtNzFDlPOd7B93HOps22iH5yDgxwJ+ah+NhO/AM+DBNJGe9AdWs5Dle2QaLWJZER/R6emzDyGr98HZ4/jCCFXW7JqHh9S7QNwPWMa/R3gTphw5BCcSnGo+cGMJNvg0C6U25vEceP7CrMO6gwUiVXcwGk/A2S2tp+bz7zjtn5kqN3fuNg+1+7SnqCqVH9C4pDPwHGlG2pfxZ1PAJy1GI3hxHZdT8Aw+rqhtPuzcDOBrIYseQ3jm8MUL3k1DSKxmPf728FuZvQlZTrc32IXjPKXeQaY8hKtxJjY7G+l2PQuB/pCE3pt4jFGXIeW0Mru5xTfwJf4khV4pi1wObqnmEOj1GK8f6NoRwuour2n031eBDsAzq113ToV/c5ZWNzXigefgfja0ECWGEiuyMjLbOto79yBYxULItYvsZh+AkTUDtzq7YBWi+1NDmPd6yvkzSxnS+p6n3d0IyC8r7V2XAevAVvq5qEvUrk48+PrZA1OC3kcK3wNMmQpMueztNrBCP9eQRqES3hB8eKxiGzyDYHyMvaCs9QUEIIRNj9WWMatybdNhfHMsxg35vDT0gkGDwUaanw2nnb5ADwBwRvMYYPmvUV+jLYUrYn3++P/s7z9Qrvg7vnEC6QAAAABJRU5ErkJggg==); background-repeat: no-repeat; background-size: auto; display: inline-block; width: 66px; height: 19px; } -.recommend-pay-icon{ - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAAoCAYAAAEUO+PFAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAKaADAAQAAAABAAAAKAAAAAD9QK3vAAAF+0lEQVRYCc2YXWhcRRTHZ24+TGJdNclCCFho0ZY8xCIKLSZKmhYhlqooCrVgUexDUVFRq/igpRDEFws+FB+0WCpqKdUiKAWlpK0t1YLgBzTFirFEMWmz2KSN+dpcf2funbv3a7ObD5sOzM7MmXP+858zZ+bOrFah5NbXu6bpOBnHyt3GxneU42wiZ/TFiyNG7jY0dJL76bzBKmrfPI/mzYEmvR5UTChW2poGA1ttI9D6XaOVy2kPu6Zmqcrnb9IDAz8bS9EKmwZCUwn9BANZmY+/V7nuFiuzE6y0Al9pkPbzqqLiSzU1NYTSjvDclOHQ0HAAb/S42ey9lN+EvWLBpLQe8mRhf4a1/LpHo4SStUtOMJtdgq+81bJalMGyigy+S9T09NlQv6lqPB4oGqXa2ipVVdWGF+63yqIkdcPTKMlwExPLyL8jn5ZOqyR1J1BynF2+kpGHlURQaYg7TofS+k/al8g74krIQv50nE4mciRNKaooVj5x6YinYNYzKRmjRNzFofx2YmXieoFX4h1pbT8cghCN6wRg+Xwf3mth0cbU5GQvcX5dXNfGu5UHc7cCATPTnJ7uRXkXPllG3zYAj+uhoRqrZ0phJhvVnhB+ZzD9gJnj7ITZ56zxacoJ9A5QvsXy9RrGrnsmziwyEA0dApN172Lk7W5z81I1Pv4yMfop4PvRyzDtTCkwCx7dnDIK02H6kwC2MMh7gK0rF8yCJhbK+NNxXoLhrwZsaCixR61xsTLB1CiWeVIUA40ynSeYHcQDXSAwCxqElBWklfj5W+RtaX0x2YmSgLMCy+XaEzspPKIPdjdhtSIsT6mfIATbRV4UMGBWX19LjH7BDno7BUhEAZg0UgENmNZZVVl5q8rl5LNwSDU1vUHZR/auBlRIETARJHwYMJNp5vM7CfazsPsIlo+yk5Zj87QYkhJgIowwDMDks5LPfw9QHyDnqH9NuQrGH4oRKRVMOirkR5I/zdtZgFaMjwG2hvJBGHbTPUz7KVgupT5oF0Ds4slMOWBWXS0fvjsxPgXYByifJ6+mnaW8hGxoJjABl30tQbsaZpthkKO+AsMHOMUf4fIxAFgH8tPIi06TviAVDgrHWYnhKqZ4IyC91A+r2tqsGh0dRbssMEEtLIrcGrTeTB4G7BMWYPlswQSwwNC09L8UEmfbyHLg/lDKZ2IWTlFA26O1hEndbMHEvDBlCyblHMHENA2w7AUQgHiKA84LTMDDgPMGM2wJbLmoS3AvTFpQMCgljq+50PRvJYexLee7U+4QJ9i1XXKvmhfJBDmt/4JBFaEsB9RcU0DOAoSDx8pKlkLOLKn3YpEjeL35Pmq9H4KjtJ8BxDwpSoIVFISc3Erbi95KC7rFaxHPaX0BzQmA1/Lx2QqxftoHyc+Ra2l/xnnYQ71USngublCWJ2Oea4OAPJnu4EBeieceUtXVu2k3k7dD+n36/0Beb77xSsktOS0V9VxcecaYjHjOWmrdDYk6vPQw5DpUTc0/anh4N7KTqLwKuSnIraf/FgiP+5/46605ZUnPhXRNNdWTEc9pPQWBo2SJPflLYBACL1KeNy/VkZFu4uhx2hnyXvJ3fJDPQfYFJiDPq9+QSSrbc5564TdCMkauHk/Ict6D+h7V2tpA+RrtrQwu95RKH6YOO3mgP8sk9pF/MnLX3aguX5bbxAg4qRvCty9ZmOWOLKvWhyCyEcsKwF/Ba19R/5HcC7m1eE+OmKO099B/0vS7rrwd7YUs79dnvazYpSYndJTYg/g+jpMWlcnILXkdVv2mLbflsbELENwCuQ3I99H/JvG3Bu/1hNBPzddzISxTlSWz5Gyfw+DH1MiIvFk3qLq6Cq5UT9D5JGRvo+8x8hHIdRF7Lq9w71+wOWwIO2Cp0sZVQc9buiYjcN1GdeWKvOYPQvgXduoZ6n9T74Tgx3j1LvQWbFkLJKK19CtfVMdryaZw3ddpnCdLLP/v5LyB4xddK525vGrkLI3kctueZHnVyVkK5ZBcNHLlkFx0cjORvGbIpZG85shZkvKfwvFi//oHSotc+Q+iPNYSwowvrwAAAABJRU5ErkJggg==); +.recommend-pay-icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAAoCAYAAAEUO+PFAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAKaADAAQAAAABAAAAKAAAAAD9QK3vAAAF+0lEQVRYCc2YXWhcRRTHZ24+TGJdNclCCFho0ZY8xCIKLSZKmhYhlqooCrVgUexDUVFRq/igpRDEFws+FB+0WCpqKdUiKAWlpK0t1YLgBzTFirFEMWmz2KSN+dpcf2funbv3a7ObD5sOzM7MmXP+858zZ+bOrFah5NbXu6bpOBnHyt3GxneU42wiZ/TFiyNG7jY0dJL76bzBKmrfPI/mzYEmvR5UTChW2poGA1ttI9D6XaOVy2kPu6Zmqcrnb9IDAz8bS9EKmwZCUwn9BANZmY+/V7nuFiuzE6y0Al9pkPbzqqLiSzU1NYTSjvDclOHQ0HAAb/S42ey9lN+EvWLBpLQe8mRhf4a1/LpHo4SStUtOMJtdgq+81bJalMGyigy+S9T09NlQv6lqPB4oGqXa2ipVVdWGF+63yqIkdcPTKMlwExPLyL8jn5ZOqyR1J1BynF2+kpGHlURQaYg7TofS+k/al8g74krIQv50nE4mciRNKaooVj5x6YinYNYzKRmjRNzFofx2YmXieoFX4h1pbT8cghCN6wRg+Xwf3mth0cbU5GQvcX5dXNfGu5UHc7cCATPTnJ7uRXkXPllG3zYAj+uhoRqrZ0phJhvVnhB+ZzD9gJnj7ITZ56zxacoJ9A5QvsXy9RrGrnsmziwyEA0dApN172Lk7W5z81I1Pv4yMfop4PvRyzDtTCkwCx7dnDIK02H6kwC2MMh7gK0rF8yCJhbK+NNxXoLhrwZsaCixR61xsTLB1CiWeVIUA40ynSeYHcQDXSAwCxqElBWklfj5W+RtaX0x2YmSgLMCy+XaEzspPKIPdjdhtSIsT6mfIATbRV4UMGBWX19LjH7BDno7BUhEAZg0UgENmNZZVVl5q8rl5LNwSDU1vUHZR/auBlRIETARJHwYMJNp5vM7CfazsPsIlo+yk5Zj87QYkhJgIowwDMDks5LPfw9QHyDnqH9NuQrGH4oRKRVMOirkR5I/zdtZgFaMjwG2hvJBGHbTPUz7KVgupT5oF0Ds4slMOWBWXS0fvjsxPgXYByifJ6+mnaW8hGxoJjABl30tQbsaZpthkKO+AsMHOMUf4fIxAFgH8tPIi06TviAVDgrHWYnhKqZ4IyC91A+r2tqsGh0dRbssMEEtLIrcGrTeTB4G7BMWYPlswQSwwNC09L8UEmfbyHLg/lDKZ2IWTlFA26O1hEndbMHEvDBlCyblHMHENA2w7AUQgHiKA84LTMDDgPMGM2wJbLmoS3AvTFpQMCgljq+50PRvJYexLee7U+4QJ9i1XXKvmhfJBDmt/4JBFaEsB9RcU0DOAoSDx8pKlkLOLKn3YpEjeL35Pmq9H4KjtJ8BxDwpSoIVFISc3Erbi95KC7rFaxHPaX0BzQmA1/Lx2QqxftoHyc+Ra2l/xnnYQ71USngublCWJ2Oea4OAPJnu4EBeieceUtXVu2k3k7dD+n36/0Beb77xSsktOS0V9VxcecaYjHjOWmrdDYk6vPQw5DpUTc0/anh4N7KTqLwKuSnIraf/FgiP+5/46605ZUnPhXRNNdWTEc9pPQWBo2SJPflLYBACL1KeNy/VkZFu4uhx2hnyXvJ3fJDPQfYFJiDPq9+QSSrbc5564TdCMkauHk/Ict6D+h7V2tpA+RrtrQwu95RKH6YOO3mgP8sk9pF/MnLX3aguX5bbxAg4qRvCty9ZmOWOLKvWhyCyEcsKwF/Ba19R/5HcC7m1eE+OmKO099B/0vS7rrwd7YUs79dnvazYpSYndJTYg/g+jpMWlcnILXkdVv2mLbflsbELENwCuQ3I99H/JvG3Bu/1hNBPzddzISxTlSWz5Gyfw+DH1MiIvFk3qLq6Cq5UT9D5JGRvo+8x8hHIdRF7Lq9w71+wOWwIO2Cp0sZVQc9buiYjcN1GdeWKvOYPQvgXduoZ6n9T74Tgx3j1LvQWbFkLJKK19CtfVMdryaZw3ddpnCdLLP/v5LyB4xddK525vGrkLI3kctueZHnVyVkK5ZBcNHLlkFx0cjORvGbIpZG85shZkvKfwvFi//oHSotc+Q+iPNYSwowvrwAAAABJRU5ErkJggg==); background-repeat: no-repeat; background-size: auto; display: inline-block; @@ -10325,22 +10447,22 @@ background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHh left: -1px; } .soft_tips_text { - font-weight: 700; - display: inline; - font-size: 13px; + font-weight: 700; + display: inline; + font-size: 13px; } /*插件付费*/ -.wx-pay-ico{ - background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAB2UlEQVQ4T62UPWgUQRiGn2/XZHYXQkgpGEtTWphGVDTiXuQgWAhiaSMiShoxiLap0oiN2FmIhSRgIhF17yBnoSCohcROgrWNonL7c7n9ZPd+vDu9sMVNszA8+8w778yuMKIhI/LwX5F5ZXzLYkFgRuEwYICvwFtUa2EpWR0M0CdyX7vTJOkScH3PpMK6NNPl+nzjQ4friryX3n7d13yB5gkKjRT82I+rGdwSvWfM+W7WBcoFDN9QVtMsFbIb+VGtK3IC55KIPtxLospnLH2c2jy1sCJ29bbAZVQvZJ3lidzA3ENYbItqFrqYIpvAQYQtVXkUudEax/nlBuNLKpJJJtv8/dCPr7VEFbMFnOokUmTO1uYPtawD9TNxJsStjJ8H6xbo7EDyWujHc7nIqzjLit7pAT6FfpyX7gVjs4p9A9GLQ7b+N5EXmLIKz3tBERZU5RjoTcAe1p8oV+ul+EGeaKrCZCTmHcpMgVPrRXbsVM/9nk+2u/fIqTonBDZQnSooixU523f8nRdbhcpaAdEbsVmpn46f/XOz84kNJlzP/ET4iJI9j6BMtOEE2Eb1SVhKVgYX6/vWnMA5iaRHo0ZylzJxBpuqOaSqdrKTfOEKjaGlF9hGIWRk/6M/w0isE5qoYoAAAAAASUVORK5CYII='); - display: inline-block; - width: 18px; - height: 18px; +.wx-pay-ico { + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAB2UlEQVQ4T62UPWgUQRiGn2/XZHYXQkgpGEtTWphGVDTiXuQgWAhiaSMiShoxiLap0oiN2FmIhSRgIhF17yBnoSCohcROgrWNonL7c7n9ZPd+vDu9sMVNszA8+8w778yuMKIhI/LwX5F5ZXzLYkFgRuEwYICvwFtUa2EpWR0M0CdyX7vTJOkScH3PpMK6NNPl+nzjQ4friryX3n7d13yB5gkKjRT82I+rGdwSvWfM+W7WBcoFDN9QVtMsFbIb+VGtK3IC55KIPtxLospnLH2c2jy1sCJ29bbAZVQvZJ3lidzA3ENYbItqFrqYIpvAQYQtVXkUudEax/nlBuNLKpJJJtv8/dCPr7VEFbMFnOokUmTO1uYPtawD9TNxJsStjJ8H6xbo7EDyWujHc7nIqzjLit7pAT6FfpyX7gVjs4p9A9GLQ7b+N5EXmLIKz3tBERZU5RjoTcAe1p8oV+ul+EGeaKrCZCTmHcpMgVPrRXbsVM/9nk+2u/fIqTonBDZQnSooixU523f8nRdbhcpaAdEbsVmpn46f/XOz84kNJlzP/ET4iJI9j6BMtOEE2Eb1SVhKVgYX6/vWnMA5iaRHo0ZylzJxBpuqOaSqdrKTfOEKjaGlF9hGIWRk/6M/w0isE5qoYoAAAAAASUVORK5CYII='); + display: inline-block; + width: 18px; + height: 18px; } -.wx-pay-ico.alipay{ - background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAABx0lEQVQ4T62UTUhUURTHf+dNgzHRe43ovoW0CGrpRhcjCu4KAvMOkmC4SmwhtGlVK3EZ5UpBaOPooiBtE0ITVqvaCOVWdCMF6vNjUeo98uY9mTfOe8wsPMt7/vd3Pu45V7gkkwpnfreASBfIKHCzSfYG6Cyq3yjmyyGo5E8DT5oE1MrUvqSYf3EO+gO0J4LU9lTOxfmc6BdZYtC9dw7S1GyM10AjZYzbUy9ynEIN9OH1L2H5+7dwnBynNkdGslhbDnVpoMARN+OGpc37qwhXIlcb0NEYJPoBZAc09oJyhPjTWK8XdKkaKy0j5SciBxWh2lZE7kaX3mC8cUr7M6DBmESWBBImkOwix//a+c8GI/k93m5fI9vSC2xhnUMyfAeC0lJBr4FWYCgm+oWwDHaFwRsrUa8e4TCM0pfWozEgGMwLJnMY9zELfj96vIlpW68IFg/vYE+6Qbox3lD1+VWeIdoJDESR1tDT98HUsuA/RXkV9o2PIFMU3dV4xNo5CmFfUXs12B9Kuw8gcx90uD5TfoCdxOTfBb6UFZE1sB0guQTAxaNPGK//EpZWJim6z2PfiHMbKIAkL29davoXKKP2d/UbaSL/RpIzc/+vE/N55R4AAAAASUVORK5CYII='); +.wx-pay-ico.alipay { + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAABx0lEQVQ4T62UTUhUURTHf+dNgzHRe43ovoW0CGrpRhcjCu4KAvMOkmC4SmwhtGlVK3EZ5UpBaOPooiBtE0ITVqvaCOVWdCMF6vNjUeo98uY9mTfOe8wsPMt7/vd3Pu45V7gkkwpnfreASBfIKHCzSfYG6Cyq3yjmyyGo5E8DT5oE1MrUvqSYf3EO+gO0J4LU9lTOxfmc6BdZYtC9dw7S1GyM10AjZYzbUy9ynEIN9OH1L2H5+7dwnBynNkdGslhbDnVpoMARN+OGpc37qwhXIlcb0NEYJPoBZAc09oJyhPjTWK8XdKkaKy0j5SciBxWh2lZE7kaX3mC8cUr7M6DBmESWBBImkOwix//a+c8GI/k93m5fI9vSC2xhnUMyfAeC0lJBr4FWYCgm+oWwDHaFwRsrUa8e4TCM0pfWozEgGMwLJnMY9zELfj96vIlpW68IFg/vYE+6Qbox3lD1+VWeIdoJDESR1tDT98HUsuA/RXkV9o2PIFMU3dV4xNo5CmFfUXs12B9Kuw8gcx90uD5TfoCdxOTfBb6UFZE1sB0guQTAxaNPGK//EpZWJim6z2PfiHMbKIAkL29davoXKKP2d/UbaSL/RpIzc/+vE/N55R4AAAAASUVORK5CYII='); } -.pay-wx-info span{ - vertical-align: middle; +.pay-wx-info span { + vertical-align: middle; } #pay_product_view{ padding: 25px 45px 30px 45px; @@ -10373,24 +10495,21 @@ background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHh .libPay-item .li-c-item li:nth-child(1){ margin-left: 0; } -.libPay-item .li-c-item li{ - font-size: 0; - border: #ddd 1px solid; - width: 170px; - height:80px ; - cursor: pointer; - box-sizing: border-box; - transition: border-color .1s ease-in,color .1s ease-in; - padding: 5px 0; - display: inline-block; - position: relative; - margin-left: -1px; +.libPay-item .li-c-item li { + position: relative; + display: inline-block; + width: 170px; + height: 80px; + border: #ddd 1px solid; + font-size: 0; + cursor: pointer; + transition: border-color .1s ease-in, color .1s ease-in; } -.pro-tips{ +.pro-tips { clear: both; - padding-top: 10px; - font-size: 13px; - color: #bbb; + padding-top: 10px; + font-size: 13px; + color: #bbb; } @@ -10443,47 +10562,14 @@ background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHh font-size: 13px; vertical-align: middle; } -.pay-btn-group{ - position: relative; - display: inline-block; - vertical-align: middle; -} -.pay-btn-group>.pay-cycle-btn { - position: relative; - float: left; -} -.pay-cycle-btn { - font-size: 0; - width: 106px; - height: 40px; - line-height: 38px; - display: block; - margin-bottom: -1px; - text-align: center; - vertical-align: middle; - border: 1px solid #ddd; - box-sizing: border-box; - margin-left: -1px; - transition: border-color .1s ease-in,color .1s ease-in; -} -.pay-cycle-btn span{ +.pay-cycle-btn span { font-size: 14px; - line-height: 25px; - vertical-align: middle; -} -.pay-btn-group>.pay-cycle-btn:first-child { - margin-left: 0; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; + line-height: 25px; + vertical-align: middle; } /*.pay-btn-group>.pay-cycle-btn:nth-of-type(5n+1){*/ /* margin-left: 0;*/ /*}*/ - -.pay-btn-group>.pay-cycle-btn:last-child { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} .pay-btn-group .active,.li-c-item .active{ background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAMAAADzapwJAAAAA3NCSVQICAjb4U/gAAAAclBMVEX///8fpTofpTogpTogpTogpTrw+fLt+O/i8+be8uLb8d/T7djP7NXR7NbE58u548G34sCr3bWU1KGT05+FzpN+y413yIZ0x4RxxoJwxYFnwnliv3RWu2pUumhMt2FGtVxDs1k6sFE4r1Avq0cjpjwgpTpRW86VAAAAJnRSTlMAEXd3iO7//////////////////////////////////////////0c8mUAAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDUvMjkvMThc7W1eAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAHhJREFUGJVtzskSgkAMBFB2AUcR2QTcEPr/fxFmmFiW0znk8CrVac9jE1CNYqoAV8JaXTbq8K7/bBVcwRVcwRVcwbRTIDqcWrjaJ3cJ0VrdjDaHh2Sb2/F43XadPuWlTfjkJersLU2+ufNFnSdb0P/5thQv6R2CzQo9Oh+AlBF9bAAAAABJRU5ErkJggg==") no-repeat right bottom; } @@ -10503,22 +10589,22 @@ background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHh line-height: 22px; } .pay-cycle-btn em { - position: absolute; - right: -2px; - top: -9px; - width: 33px; - height: 19px; - background: #ff7300; - border-radius: 2px; - font-size: 10px; - line-height: 19px; - color: #fff; - transform: scale(.9, .8); - font-style: normal; + position: absolute; + right: -2px; + top: -9px; + width: 33px; + height: 19px; + background: #ff7300; + border-radius: 2px; + font-size: 10px; + line-height: 19px; + color: #fff; + transform: scale(.9, .8); + font-style: normal; } -.pay-btn-group .active em{ +.pay-btn-group .active em { right: -3.5px; - top: -10px; + top: -10px; } .paymethod{ margin: 0 auto; @@ -10528,12 +10614,12 @@ background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHh height:175px; } .paymethod .pay-wx canvas { - width: 160px; - height: 160px; - border: #ddd 1px solid; - margin: 6px auto; - padding: 5px; - border-bottom-color: #00c800; + width: 160px; + height: 160px; + border: #ddd 1px solid; + margin: 6px auto; + padding: 5px; + border-bottom-color: #00c800; } .lib-price-box{ padding-bottom: 5px; @@ -10549,87 +10635,87 @@ background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHh margin-right: 10px; } /* 企业版支付end */ -.commonly_software{ - height: 28px; - line-height: 28px; - margin-bottom: 15px; - color:#666; +.commonly_software { + height: 28px; + line-height: 28px; + margin-bottom: 15px; + color: #666; } -.commonly_software .commonly_software_title{ - display: inline-block; - padding-left: 5px; - padding-right: 15px; +.commonly_software .commonly_software_title { + display: inline-block; + padding-left: 5px; + padding-right: 15px; } -.commonly_software .commonly_software_list{ - display: inline-block; +.commonly_software .commonly_software_list { + display: inline-block; } -.commonly_software .commonly_software_list .item{ - display: inline-block; - margin-right: 10px; - padding: 0 10px; - cursor: pointer; - border-radius: 3px; - transition: all 500ms; - position: relative; -} -.commonly_software .commonly_software_list .item:hover{ - color: #20A53A; - background-color: #f5f5f5; -} -.commonly_software .commonly_software_list .item img{ - max-width: 22px; - margin-right: 5px; - image-rendering: -webkit-optimize-contrast; - vertical-align: middle; - margin-top: -2.5px; -} - -.commonly_software .commonly_software_list .item span{ - display: inline-block; - font-size: 12px; - color:#666; -} -.pro_introduce_content .item{ - position: relative; -} -.pro_introduce_content .item .success_icon{ - position: absolute; - display: block; - left: 0; - top: 50%; - margin-top: -8px; - width: 6px; - height: 12px; - border: solid #20a532; - border-width: 0 1.5px 1.5px 0; - transform: rotate(45deg); -} -.pro_introduce_content .item span:nth-child(2){ - padding-left: 14px; -} - -#bt_database_table .tootls_group .pull-left button:nth-child(4):after{ - content: ' '; +.commonly_software .commonly_software_list .item { + display: inline-block; + margin-right: 10px; + padding: 0 10px; + cursor: pointer; + border-radius: 3px; + transition: all 500ms; position: relative; - left: -76px; - border-left: 1px solid #ececec; +} +.commonly_software .commonly_software_list .item:hover { + color: #20A53A; + background-color: #f5f5f5; +} +.commonly_software .commonly_software_list .item img { + max-width: 22px; + margin-right: 5px; + image-rendering: -webkit-optimize-contrast; + vertical-align: middle; + margin-top: -2.5px; +} + +.commonly_software .commonly_software_list .item span { + display: inline-block; + font-size: 12px; + color: #666; +} +.pro_introduce_content .item { + position: relative; +} +.pro_introduce_content .item .success_icon { + position: absolute; + display: block; + left: 0; + top: 50%; + margin-top: -8px; + width: 6px; + height: 12px; + border: solid #20a532; + border-width: 0 1.5px 1.5px 0; + transform: rotate(45deg); +} +.pro_introduce_content .item span:nth-child(2) { + padding-left: 14px; +} + +#bt_database_table .tootls_group .pull-left button:nth-child(5):after { + content: ' '; + position: relative; + left: -72px; + border-left: 1px solid #ececec; } /*node项目管理*/ -.node_project_dialog .mask_module{ +.node_project_dialog .mask_module { position: absolute; height: 100%; - background-color: rgba(0,0,0,0.5); + background-color: rgba(0, 0, 0, 0.5); top: 0; - width: 610px; + width: 630px; left: 170px; z-index: 99; } -.node_project_dialog .mask_module .node_mask_module_text{ +.node_project_dialog .mask_module .node_mask_module_text { background: #fff; /*color: #000;*/ - width:280px; + width: 280px; text-align: center; position: absolute; padding: 15px 30px; @@ -10638,7 +10724,7 @@ background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHh margin-left: -140px; border: 1px solid #efefef; display: inline-block; - border-radius:2px; + border-radius: 2px; } .node_project_dialog .node_project_pid_datail h3.tname { margin:10px 10px 10px 0; @@ -10659,7 +10745,7 @@ background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHh /*文件内容查找替换*/ - /*查找textarea样式*/ +/*查找textarea样式*/ .replace_content_view .replace_content_line:first-child{ position: relative; height: 36px; @@ -10680,17 +10766,20 @@ background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHh line-height: 34px; padding: 0 4px; } -.replace_content_view .replace_content_line:first-child i.history_search:hover{ + +.replace_content_view .replace_content_line:first-child i.history_search:hover { background-color: #f0f0f1; border-left: 1px solid #e2e2e2; } -.replace_content_view .replace_content_line:first-child .normalBtnStyle{ + +.replace_content_view .replace_content_line:first-child .normalBtnStyle { position: absolute; right: 0; vertical-align: top; } - /*查找textarea样式 end*/ - /*历史搜索列表*/ + +/*查找textarea样式 end*/ +/*历史搜索列表*/ .replace_content_view .history_search_list { position: absolute; top: 36px; @@ -10711,7 +10800,8 @@ background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHh background: #f0f0f0; color: #555; } - /*历史搜索列表 end*/ + +/*历史搜索列表 end*/ .replace_content_view .checkbox_config { color: #666; @@ -10796,7 +10886,7 @@ background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHh } .replace_content_view .replace_content_line { margin-bottom: 20px; - } +} .replace_content_view .replace_content_line .tname { display: block; float: left; @@ -10895,25 +10985,25 @@ background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHh /*文件内容查找替换 end*/ /*删除数据库弹窗样式*/ .promptDelete p { - font-size: 14px; - } + font-size: 14px; +} - .promptDelete .confirm-info-box { - position: relative; - margin-top: 15px; - } +.promptDelete .confirm-info-box { + position: relative; + margin-top: 15px; +} - .promptDelete input { - width: 100%; - height: 30px; - padding-left: 5px; - } +.promptDelete input { + width: 100%; + height: 30px; + padding-left: 5px; +} - .promptDelete .placeholder { - line-height: 24px; - left: 7px; - top: 2px; - } +.promptDelete .placeholder { + line-height: 24px; + left: 7px; + top: 2px; +} /*删除数据库弹窗样式 end*/ /*站点删除安全验证*/ @@ -10994,12 +11084,453 @@ background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODIiIGhlaWdodD0iODIiIHh left:50%; top:50%; margin-top: -25px; - margin-left:-225px; + margin-left: -225px; background: #fff; box-shadow: 1px 1px 50px rgb(0 0 0 / 30%); border: 1px solid #eee; - border-radius:2px; + border-radius: 2px; font-size: 13px; } /*end*/ + +/* site的tab切换 */ + +.tab-list .tabs-item:hover { + background: #20a53a08; +} + +.tab-list .tabs-item:first-child { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} + +.tab-list .tabs-item { + display: inline-block; + height: 50px; + min-width: 100px; + padding: 0 20px; + line-height: 50px; + font-size: 13px; + cursor: pointer; + position: relative; + text-align: center; +} + +.tab-list .tabs-item.active:after { + content: ''; + width: 20px; + height: 2px; + position: absolute; + left: 50%; + bottom: 0px; + background: red; + margin-left: -10px; + background: #20a53a; +} + +.tab-list .tabs-item.active { + color: #20a53a; + background: #20a53a10; +} + +.tab-view-box { + padding: 20px 15px; + border-radius: 4px; + box-shadow: 0 0 1px 0 rgb(0 0 0 / 5%); +} + +.radius4 { + border-radius: 4px; + box-shadow: 0 0 1px 0 rgb(0 0 0 / 5%); +} + +.radius2 { + border-radius: 2px; +} + +/* 设置页面 */ +.layer-info-ico { + display: inline-block; + width: 30px; + height: 30px; + vertical-align: middle; +} + +.layer-info-title { + display: inline-block; + vertical-align: middle; + margin-left: 15px; + font-size: 21px; +} + +.explainDescribeList { + border: 1px solid #ececec; + border-radius: 6px; + margin: 0 auto; + margin-top: 20px; + margin-bottom: 20px; + background: #f7f7f7; + padding: 15px; + list-style-type: inherit; +} + +.agreementBox label { + font-weight: 400; + margin: 0 0 0 5px; + vertical-align: middle; + cursor: pointer; +} + +.agreementBox input { + height: 15px; + width: 15px; + vertical-align: middle; + margin-top: 0; + cursor: pointer; +} +/* 首页专业版推荐 */ + +.recommend-top{ + height: 50px; + line-height: 22px; + display: flex; + align-items: center; +} + +.recommend-bottom{ + margin-top: 10px; +} + +.product-ico{ + display: inline-block; + margin-right: 10px; + width: 90px; + height: 20px; + background-repeat: no-repeat; + background-position: center; +} + +.product-buy{ + margin-right: 15px; + display: inline-block; + font-size: 13px; + color: #666; + vertical-align: middle; +} +.product-buy span{ + font-weight: bold; + color: #fc6d26; + vertical-align: middle; +} + + +.pro-bg, +.ltd-bg{ + overflow: hidden; + white-space: nowrap; +} +.ltd-bg{ + background-color: rgb(255 251 241); +} +.pro-bg{ + +} + +.pro-ico{ + width: 70px; + margin-right: 5px; + background-image: url("/static/images/pro.svg"); +} +.ltd-ico{ + width: 70px; + margin-right: 5px; + background-image: url("/static/images/ltd.svg"); +} +.pro-pay-ico{ + background-image: url("/static/images/pro_pay.svg"); + width: 130px; +} +.ltd-pay-ico{ + background-image: url("/static/images/ltd_pay.svg"); +} + +.product-expire{ + display: flex; + color: #666; + margin-left: 10px; +} + +.product-expire.permanent span{ + color: #F39C12; + font-weight: bold; +} + +.product-label{ + display: inline-block; + font-size: 13px; +} + +.product-label .item{ + display: inline-block; + position: relative; + margin-right: 15px; + padding-left: 20px; + color: #666; +} +.product-label .item::after{ + content: ""; + display: inline-block; + width: 12px; + height: 6px; + border-left: 2px solid #20a532; + border-bottom: 2px solid #20a532; + transform: rotate(-45deg); + position: absolute; + left: 5px; + top: 6px; +} + +.recommend-soft{ + position: relative; +} + +.recommend-soft .images{ + text-align: center; + margin: 22px 0 10px; + height: 40px; +} + +.recommend-soft .product-name{ + text-align: center; + color: #999; + margin-bottom: 10px; +} + +.recommend-soft .product-pay-btn{ + text-align: center; +} + + +.recommend-soft .product-pay-btn button{ + padding: 3px 8px; +} + +.recom-iconfont:hover .product-close{ + display: inline-block; +} +.recom-iconfont .product-close{ + position: absolute; + top: -1.5px; + right: -6px; + height: 18px; + line-height: 18px; + width: 60px; + text-align: center; + background: #efefef; + opacity: 0.5; + transform: scale(.8); + display: none; +} +.recom-iconfont .product-close:hover{ + opacity: .8; + background-color: red; + color: #fff; +} +.recom-iconfont::after{ + content: 'AD'; + height: 18px; + width: 40px; + line-height: 18px; + text-align: center; + background: #efefef; + display: inline-block; + top: -1.5px; + left: -4px; + position: absolute; + opacity: 0.5; + transform: scale(.8); +} + +.product_view{ + display: inline-block; + width: 350px; + border: 2px solid #ececec; + border-radius: 4px; + cursor: pointer; + flex: 2; +} + +.product_view img{ + width: 100%; + border-radius: 4px; +} + +.product_describe{ + display: inline-block; + padding: 5px 0 0 30px; + flex: 3; + color: #666; +} + +.product_describe .describe_title{ + font-weight: bold; + font-size: 18px; +} + +.product_describe .describe_ps{ + padding-top: 10px; + font-size: 13px; + height: 110px; +} + +/* 绑定账号 */ +.libLogin { + width: 400px !important; +} + +.libLogin input { + height: 40px !important; + border-radius: 2px; + width: 100% !important; + font-size: 14px; + outline: none; +} + +.libLogin input:hover, .libLogin input:focus { + border-color: #20a53a; + outline: none; +} + +.libLogin input[type="text"], +.libLogin input[type="password"] { + padding-left: 10px; +} + +.login-button { + border-radius: 2px !important; + text-shadow: none !important; + background-color: #20a53a !important; + box-shadow: none !important; + transition: all 500ms; + font-size: 14px !important; +} + +/* end */ + +/** 警告框标题 */ +.layer-info-head { + display: flex; +} + +.layer-info-head .layui-layer-ico { + display: block; +} + +.layer-info-head .layer-info-title { + flex: 1; + display: block; + width: 0; + line-height: 30px; +} + +/* end */ + +/* 协议 */ +.agreementBox { + display: flex; + align-items: center; +} + +.agreementCont { + flex: 1; + width: 0; +} + +/* end */ + + +/* 文件上传 */ +.upload_file_container { + display: flex; + flex-direction: column; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; +} + +.upload_file_gourp { + display: flex; + justify-content: flex-end; + padding-right: 15px; +} + +.upload_file_gourp button { + font-weight: bold; +} + +.upload_file_gourp button:nth-child(1) { + border-color: #cbcbcb; + background-color: #cbcbcb; + color: #fff; +} + +.upload_file_gourp button:nth-child(1):hover { + background-color: #c9302c; + border-color: transparent; +} + +.warning_layer_box { + padding: 20px; +} + +.warning_layer_head { + display: flex; + margin-bottom: 10px; +} + +.warning_layer_box .layui-layer-ico { + width: 30px; + height: 30px; +} + +.warning_layer_head .cont { + flex: 1; + width: 0; + margin-left: 15px; + line-height: 30px; + font-size: 14px; + word-wrap: break-word; +} + +.warning_layer_box .details { + display: flex; + align-items: center; + margin-left: 45px; +} + +.warning_layer_box .all_operation { + font-size: 13px; +} + +.warning_layer_box .details .left, +.warning_layer_box .details .right { + flex: 1; + display: flex; + align-items: center; +} + +.warning_layer_box .details input { + margin-top: 0; + width: 15px; + height: 15px; +} + +.warning_layer_box .details label { + margin-bottom: 0; + margin-left: 6px; + line-height: 1; + font-weight: 400; +} + +/* end */ \ No newline at end of file diff --git a/BTPanel/static/css/xterm.css b/BTPanel/static/css/xterm.css index 7ddcc2d0..d43af49b 100644 --- a/BTPanel/static/css/xterm.css +++ b/BTPanel/static/css/xterm.css @@ -35,7 +35,7 @@ * Default styles for xterm.js */ -.xterm { + .xterm { font-feature-settings: "liga" 0; position: relative; user-select: none; diff --git a/BTPanel/static/img/ico/index.html b/BTPanel/static/img/ico/index.html deleted file mode 100644 index 35c63e8b..00000000 --- a/BTPanel/static/img/ico/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
-

目标URL

-

发送域名

-

内容替换

-
-
    -
  • 目标Url必需是可以访问的,否则将直接502
  • -
  • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
  • -
  • 若您是被动代理,请在发送域名处填写上目标站点的域名
  • -
  • 若您不需要内容替换功能,请直接留空
  • -
  • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
  • -
-
\ No newline at end of file diff --git a/BTPanel/static/img/index.html b/BTPanel/static/img/index.html deleted file mode 100644 index e69de29b..00000000 diff --git a/BTPanel/static/img/soft_ico/ico-alioss.png b/BTPanel/static/img/soft_ico/ico-alioss.png deleted file mode 100644 index 730007077017f0098c6080e3f089ce2634f5bb0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 516 zcmeAS@N?(olHy`uVBq!ia0vp^#z3sj!VDw>PUKDjQq09po*^6@9Je3(KLE)E_=LFr z|Nq}Enuj2oS9t#dPz!HKkY6x^ppvmmXj*y4%vHP3xWDqe1(e_{@Q5sCVBk6o!i*-G zDyxBlG9|7NCBgY=CFO}lsSJ)O`AMk?Zka`?<@rU~#R|^BW-s<^>S17DH1>3H46*1< zPFNtbV##)P?u5w+Ju$`4&dd~ToMGg1HT}SY5T&*$*CjP9o;Ii*u4CitTjcpwVaYj_ z2bWsb>9R$>;@OmYraEAWn4)Llf)jHr+f-7UwMwHH6+@e~TA#QbGtCGRPB2n?m|>J| z=A5dOc+S~E_?5A*#D9x9rOzy8O;_7=V5a)Ppj!s-G+A4+{TgM>*-yTb5s6(>@L6_( z55w)FoR4Ll`PL_Egqb$XX?dg{ku!%$U3G@FT9bvrZ1aUK(M+4Jq&W)sOPw-y2rI9; z9>kWWoc-j$hBoVe63rq#oQAPNdRrwI>NGH3JuUp;o1A;UE4#&JLET81cIg6{Y0(Kx z!inoFWEuP=?gcE}V4>l(MD3W4!v7XV=44TiM=6FuQYi^t5w@~VYTdrCi+$V2Bf`iq Y_trxBDDT&gfWge*>FVdQ&MBb@0FQ6CJOBUy diff --git a/BTPanel/static/img/soft_ico/ico-btyw.png b/BTPanel/static/img/soft_ico/ico-btyw.png deleted file mode 100644 index 8f97a9c153bcaa3add867f5ca44474a2898df060..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1813 zcmV+w2kQ8VP)Tq=k98?MM|rOn94>UBdXFA6r$D^ z)vg3xgCAT7G$9|gCAqP{4+%8bkd_*ZX-XQ5alwS9(9#qMdshlZ4}zw|4*_kkymGPK zxTGa2xb;Yt6_qGdP`TNiUCrsk?&xE+TJ6r(rS$(a+`0cb=Rb4L$Gsz}N-2m)$?;OK zMq*)t>0XSn)(2f(dQe|K{mJvm)rucf1&aL^KSDa=-}t-}Fg-@;LFobM0a{B;{ky_V zfm@2)tgACO?>>L+lN$VAN6kXo61|56GcNFG)y82}m6zs+Qt@iJ+n!bAx2g64f_&SL zh&-1@5x(PxyBn$(BJ%s%Ov+4c{L-ji$eN^pPyHGccP-_YTMktVxp(aPpAr4gr#;&~ zb|dMNTNXi|yf*;5T9UeQ-KV`>;ZI1W{wm@0ooZ>~gqM8Ts{p=)IQ9Q*47v3m0pY5M z^_i~%vwm#uhN@!38i_TvCnDd=1|LZ){Ip72qxQhetQGHj(HiM#u9CYeDlUlDNUTXi z&W8dm3e*3p3w^60lRkhkCxOG1_&N$XkfezFb9-QB(uzk;BXUk*RgC+%ee{Oy*YRS% zD_&X}%AOHuLFB5q?)#t1<+=+QC~1wvnh2)5F{T*^yGqWeyO0fJ|F}4Cdl|qQi#(<( zL4i?$z1;Eteymfq?u7 z<%r~C^K15C%D0%B+W5A3^jQ(c3y!SdggWfuIX7g0S0pfD|}zeQvT)n<(OCaQ-4RG66C8#>CtWZBeRD4Nd- z>{p=^m__-67&l(r1^h=2AGR1zM2{<+Da625s4S_tug~qP|3z65Q?X$CV&;?}ui>>i zDocnv39O16XH2yEo2Ufha;p0Sn9_{x4`|S=9k~LX0u`;Rw8K%|-we4Q1wl zHvt-Q*)xco&U+&SglB*r$Okbh=OllNY~5$AK;BuE?jloLmg4-t^z^>5{T+(~x0hQl zMY{q44wUk5*YvyRO03Zu>w6a%N3>`|Bz~zfWqnFq~~mw%u4|K#`Zr7 za(okKeYueiZ)tB7RuLu}vY87^#kQ=L)=SYYM1BaQZHsN^$z_*TzR<~w<>si$E9(M; zoM`?;adGb>D*z!kbOPDfJt}m0*Y-Au&49^dyCfefBq4tlR4d#{Z`)jzFwdk(J$^KLSeyOlczmS) z6g!9j2pE3THrC${U=Llt;h6M<%0~PYVN{&ZQWCz+hTjF?T-;2Je*m>s4`!^4}1$329gLx%r`)l7U7XH5hp#sG~)has700000NkvXXu0mjf DfA40W diff --git a/BTPanel/static/img/soft_ico/ico-deployment_jd.png b/BTPanel/static/img/soft_ico/ico-deployment_jd.png deleted file mode 100644 index 5a46186bcf31ac52e03226d93faf8023836f5358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 553 zcmeAS@N?(olHy`uVBq!ia0vp^#z3sW!VDxA&b#jjq?n7HJVQ7*IBq}me*okf1o(uw z{{R2Km=ths!Al#UUV)MzzhDMIC1aP+wDOLbt9GBc`|f|)R7O#t9A|+?WHAE+w-5+3 zUb-L1;Fyx1l&avCS(I9yUzA;};2doBV$Y@?1_nkSPZ!4! zi{8>nzJ7-d1X`CfGG#@S^oD4PFGxDgR>Y{1$6%h}=Wt`wW45OpcNTcaENHcL(GpSwe<;Pb?=RH5yoT(03 z?{X(lkI)+#QOkN**n*wW4WKl2CauRVvkIi zIK9qPt_XiA-Pod_`Jj2r@`B0I`-T(dlfq|p;ST4$bZ4ciLxgg_-5a* z4^U`b`Td#0BQcc-dGE{px=S`Vu?F4ne5)!kL)+do?481*)i>A=g_ B-RA%R diff --git a/BTPanel/static/img/soft_ico/ico-diskquota.png b/BTPanel/static/img/soft_ico/ico-diskquota.png deleted file mode 100644 index 61bcabdea6d7d6df358b4fd7955296bd845451c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2971 zcmbVOc{o&iA0MeKErgbS2($%n zg>w|HFyXBxzESwBJ{`&uF28ZjUAc!DzT6NZ8vq@oFh~FxPb2yPjsTGo%6bYIfk2|c zR3}%iE5QasX3%tqs~8;~jVWY1htJ2FqL8qaX}Ga1g06_ zPqhkT1Bb(GoycJUWHbeWH31v(FhT<~fJ+4PXn}MNhGz`TrLv>g@%NL=!7737;HZ%9F0apVF)M!p)Evcb3*A{B2SynQC_ov z12|+hmC2-)Q*X;{VB~ z{J;5w3PnLzo8`ZnXYG}+BUiV7je_v-*DwKe;RLaTqcU^XP67lHtHt9EI`Ia_-8|2E zI&b$15KMDc#yTo!d70Eh0o$xun9NHT!Pq_*9_&(#bdiOu!t3bLgas9;{e3dBpA3~u zd`P-+yW+d0H_0h#*pxa+{<-7NlX&aL3CPUs?;#xn;qw!m&c!Fvx$its4_|THjs1p? zfAbn%nCB1XYC*m_EUPWVN6|rZ-cfb!`zLQd*bVLD3 zS1MCayl?0o0pAP4CT(#Ki&IU`%Xa&mp%kS;p}A`t^FyS@dBgPbE2$D08?&!Qzl{{^ zNqO2h%}U)?xa`*Q(YEc~Cxr^;Ih?YWtoo0JKDgDYd1m9b z*13AByPSyllxXWD>k0Yc#~q95>yB2&Jwy(CK47jMi5upC#KoetIuL1JLDB(u z!JK{NxLo+6V%e>M&N>3}G_;9l{aB_?CH@X<_zZgZbc7uj0U0y^rC7COIi^B%x;%!i z&x_n4Hfkam)l^N40&qDp`-*lX8F4A9-Rabv! zxc;{4eLoMI0^b8KvVT(ozHVDw2KFAZF7!UhZ^j@@B%chg`v|)>0#?KlBBy%57XDXs zQTt;*1X%sPM5&XR&8xtorGwI3_9eV9EcKMSrng+I}6EOgA`I?|(1zP@JLe7{S4E&_{A*U1Tul z#G^!ULz(yZ&(g;N2kNt&9_Q5cd^=U_z?mN^J z=_|!B^BZh7%(|)q@=KrELWTBGtf^Qu~Uwc=ZzNR^uE$aUr4|~ zVtmh*k7YW0@v!EZX6=_XkB`UlJ2vfeoezE=6I1cK23-BZY+ho<-uP!>RhzP$GHow^ zl=J4xqpC_9jznKf8>(+B;*ajx-dce#k%y=(xffJqY=i?1P`n4#{PM%_kx?c6N~dnW zlD^{~*LQ(puaL9AZL^SL*`59zF|{wdBAi>EpV-gYLk{=cny!M`QnRxxF`n?Gq&KfF z96Enqdu&8O&i7S*G&5Tgv@xkM1vUV&wn%Rg$@-!k-ffO8%i&K#E?Y;iZnI`cOTCkA zZ%6qLOyeTd6PBmBP1AKN{IQC6D=&{OgqP%_ z+MFgf-!$ImDtg)f$msWenJ0N?W*P!h)OStWbxjZpZv;JN;B)&MbkcGnF7;%PUSz2U zzZIDq_jyyRtv}m*3q-mj?qNIT4S5bg8wyB$81UnWnt(@?%J5+-*YYb7}( z7M-_P{#|5FaYHUP;cbWVqg?n{RRNlhYxAK&Iq4now$Y=h!PhE@uWaa`_781uVq@(# zR%BEXb8(>`q1Mxq<~Lb&GVtm!$*3$-RqJJvgqHFU_exTWmRMpDccAV_evh@<)v&?7 zxUH#0eQ6}WJBuLelnXhJd&R;DMg(Gal}X{DXZN1=eI}%5i)|2C3(|}4GJW4j zoQ!O$tqyVy#o_jv>d%_eL6NTP-REkV=eA$>yrwkV)u@$xm0u~xx?1Cxu18BmZwX_A zpTiX|JPq12rMCfgAxlAQ&LMX3YvIeW{PC!9XV*?GjatR#n&!+Yf(~fLx18W!U*&g| z+t43UKQJE`tG5Cn<``jseBa5>`QxRR6R`}%G*S!?Y zpHEDt45kU^y}ioCaet8R6V>WYzGb`kdAvy{k!NfE%3DEO6KGA2!QS{PXQ|2&5l2O} zc8kgchQ<=vniHRF`i6RE+KYF9M(CBc|^YXE@)d5E*5h zj60t&g4R(ih4#or1|2?q7po6)CHmbeQ@U5Yb3?`_hNp6TvHCf`qRK{=|D-@Nac8Hm zt?lfe&kU!-PYkrV-s|pkY1TOCzB8a_-!#nBce6BI)LQ$7MoNbpEmh>n)+gE#)r+@17RnFcCXy*U*05Mki9!Vu`y9uaDGL(R%B#F dz6ra27l`gnb@eP}h^_uE>_3HhZyWQx8y`7)YIS zQEFmIW`3SRNM>#-gQ1bWk*U6+d4K1}bOr`SQBN1g5R2Zo7dLt_I|{Hps8^o&c->7W z9i1m$yYD!6+?W}(amSvhCWu-?d`t zjh7+THD}(P*ZB}{zvi&X-ggZ5dfB$EZ&;giXQirg>-5;%Wc@ diff --git a/BTPanel/static/img/soft_ico/ico-domains.png b/BTPanel/static/img/soft_ico/ico-domains.png deleted file mode 100644 index 9b4b1fd130540683d9f1fddcf87e8c334c5a61ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6372 zcmchck?xXMNd=^lT9%UTk}ioA5Lh|{B$W;k z1O%k>-RGR=UwEGnGxOoPXXctS=bkz9o9O3SYLsLwWB>qw5)M<=#kB+fDI`R=y2_|J z2LNEIfGa=K_chzgCOv=m)2P4GG!gcuP#OI|otaK0J!vR;gC#RLzYUrAz$-$1Tb234 zbNvsZB?Vl3EH))15C~SK7^wMe>-KDQ;h(&HjZam>%#wZf&AZ$!na+URJC>!B>XSw1 zDj$1pn*pOOJ6W`en2Ozz$iMwZkLV=6>V*uQN)eTZ&M&U zB8m&O_4KL=$GI^cP!9NLiwpgq7@;EMypKY%!3{ZX&CDlTha)yj7{CV{XgU6ECG;5j zSPT9-<=74`lyrPZ_6|9LT>!M^6k!l;(MA8>JP>(&0fcKOAGZKy)C3YG0iMs`KFP?o zqG66EWI}-Z9d!E;%`Ue4L{0Js6zItgKSLpd;HB)vPtqjI64cu&itO3f4@&U{dLgo} zTdYBaAX%PmHL&dN9DxX-LbbqlD_ZcaOZPutE%aAzujV61E+h+byzv6laB{_m~$l4jq$Wm z$mVpvxr(a{K-c-18~ofLogWAeZen9)3ViiSS2H;8PEJZmC#}KXNMqeO5}}@^PtW-} z4LL7ZY*F%3eI*55JzXD~>N<-zkOFD%fE2o6UXott7Lmid?JJ}|5u%9U-J@S^NL`Y? z3V_@5)F!6z242J4924+_za_y|O2Lcv=o~&DU2;x>i8S<+L9Vyh=qo?YP<`t~HyB zG>a#i+FY|O3#oA=(H*jn4^AZDe z&;FWckQ7tkBWtLI;@i-e;<}kkHjwdJy%fVQDi9{m6jh)=br(0pIuS(y>tj>QB-HV- z0H~?daG6~&6X&qec6i~F|ECZ-?SEhSjyroEHUCY-KK4&bMC!mFX&-2_dL9HmE2!!1 z3!PhRcV}RC6tnsU?1=-vyE#}cD#R^Dvt|7mAL)@ohGpr++JidczcoH8R3CSo+D3M# za7kE_+H&!ew$fWdF7e9SoeDMyAh~Lo9+d z1P6 zqEa|6fv%{6bF287I3uMVzqx2jFXBi+_O^|_aNMIu2Wry&W$Gwbk6~aH!R_@wK<1?s zJRuQzg1_O)iU76m3q*?xQHM~VLO(|AXyo}_dcb!OewK_4I@1Vxu>I6azTZCh_)FN>;FUeXKfZ_+^bK{a17Ci5LD;~5$i!*9CN!cuN2DKFNbj=FT31!PYZSjKF6 zN;pJ}un3GVrbq~`-xugv(@k~jHejP`Bx}6agg)SK4qOmbKzn((6OQl56r(DrsrW2p zy!B%4oWc1{0{Lq2HuHit1Tj93;4$Qw7~KJ%yi3-PpAk_gKug7X@{=?gK%U0itG8t$ zKkpppf?xUCz*;JUCfJqwJah5xvv({%b|`}h8!9?9cVLRF8smX#ZaCys97 z%n5Ed=8}%-(*z&-sC0t2F?4$i$hmS+jI`PzFG6wnsjAUXuI#;)x&Hl+Lx-4o_SVqB zzVr%L^0-j|X|&_1qCEFD@#9ubiS3`_g7qV3^1SheZ|CJJx(S(P{rRt~kIB*d^)G_w zlJ7G}7_Lbrl!~hLf3gYIXRuG|6DH`8Xs{h>Aa^md=rozk<^8dSh`q5k=du)=2(@;0 z`T1(=OHcd8=X479t!H)5Qz%fM)+(3g2qEp7lf9xQcKNkmqiL6mBGuO%Lq)m=B3W9A zXcOXxZw75n9ftj--l?eZw}1DudK0?*B`n9g=aRJ zAa%9Q&k0avRnhzlV8Pu^dF+sy-?O;5VMJ4l4?dXha(mbVblHHVwatFCAXIDRGLl1$`*w!&ZD}Cy#JCXE&XQ}doZe(b1jQqr!1J;^h6+U@7=y@D$Bh*9 zfG8cL(ZD)}_9cE_lgQ<82>Fpwfa$4Tpq|5=p!np#CZyd=SD#Sh!-Fpw6{d5PsNYAK zpF#aupeD$K&vKf@;VPOv>!IGsAL-+h>R?tmRfOrb&jdBhU$O7rb!E|G(V@TmPQgdTrplkldxL3I3;mw+%*7phRyS?E3>UrCl}Ev%!M1lwsIH0 z3L9qha;<+hR+^p~oAZ=O&}&1%cwyBq?mi+wgqgFqz*#quaL5>>IVp$rh#3hts=pY9 zoNLol-o}9iu}#tBkIzsdT>gn=$}T)DxfWoMS4r$q)bc!eL3ne@V};X1vzs;_)bXQ~ zOvp-f(PSE|5wUN&k0(HaAyEA$!iC{7zs?|#0Koq{7p<5}`Jb~6ip2R^gD|d{8%gx6 z#}_E^C^5brgP`w=v=R~l?xeR?S7}RRP ze`h5mA=PvM=+7I=tTYT}`6eca8SXmTS3Hi$Y`*lf{W zx%nV2Oua?JJ&C_6szd>wqCfM3(Bh-Hx)VCZ3>)1bHoUXA0m3?m}xS+BZ?zwy`Z!>&G86 zRo^_xlN$n4+0wpw*(f_UCX~P^hxP>Puwq^cYY0|OAQ$yv@G4$+rvM|-^GF+aXtsjGbO1?1j+EiFLhaAS{>aL45u7j-p5n~L{S`M2=%S%MU zP8tLr4cW1(47NA}z;rZ8k5{arF^2oYA)#H8!nU-1+J9b#>{Z>5dE0tH=0)uY$7UAg zm~zgM{|yFxu)}P%66{+W>*I4Ky1X;iV1uu+Qu<|c^*pG)u8$7U5KPKKB!>GXbeUxT z^wzqFlJb7c;8^r7nfxG^%kvJ+_Mb%(sMVHP^2W4|k$ZvGjPB(RsALrC?&Q>02{Eeh zZJ*pzIIUaZrN=6lmJozaq!LwCDq~L%*z0_e@0JoEVgtDIE?$zGG(;BB9%=Ut! z&B--s5|$dl=i1>DsSe*^eQeQUt0aiG2*zmu37b`OS>l02%mu#Cmz*E+y-rgfBlWEL zF1qc^6Ood74s{3R>g_xyd0k>Wkecbv^?eS5MRW_E{Wtzkgfz$FZ^Jcm(F$Wdf*=8| zADQSctunOPM@nnY#;1}TuSNv4k`rB^XUWQ@8p#FE>~=v=$iR%8Gcb&QVd9@R@}NC! zcw$@a&4mpU)-y}VA;LYBqY!4-mnA1YyjH*gU+e3U`!?{KWLSDE4$2oena`*I6CE+G z#WHY-wV2(>@)i=Y2*oQxxk!PMMF5Vp?*oRCXv>>f$fDr%1^Zn*H8X86^g~(SVYwnu zF^@?ahArk0TAgw>)bycY`8(+RL`+BQmTh#(Ae2(&{(dy1;->N<=iE z4X*SXi9)Yv&CU29Vu9Xt@w5O!i=+7S9as-SZ{rIKw^_FOMaUla)j&^v<2OhkgYh6C z2t)nn1NU|DsYS41n>)#9Nd_SQUzwIv=7iQGiy&WXluwAiI{zjnV+#>WEbzi#6q7*9 z-ke5yWUL!IEFK?ajcp(_ng9}Xz5~=PQSTkCcCkujI^h!p-DiI%l8sUrE6z}pif+#d zJ$EYAue~~8n4aAG-e28Ttd(QmY{<&l2xsRKd|pB?GF?O;M^nA)yELXUX9Y{_iEp?> zNIX*OH5lpV^D}i@Yhi_5UZ)fF?K4jxU2SU!72J6(0$yLJWWZF$T_*eYPz5`9Xf=;nTgmp~Ml)(nVFOt9&d@i3~Ilm%->K0`$kjVpLC0i}6MO{t%A zu&}b#`#PKYLjDxkjr^(1k2X`Th;{?N*wB_llmg|;9ua?03pFk;Qf5Y-(9$Ji(?OmT z8=*$VQoz6mTB}(Pr^@1GD#`-|vP8YpV@{!sgxQAt4$4I&SV1`q= zfnohtp&I|~-~hT?Ey72&Aep}QbHgYUSZ}qK5jy;}V`ic(k18Jo^*G>(+gJ`aXOyj@ zrR~Pk79Rh_En?J1ssfv?zj*2USyIQP@EIy5Z z0LELT1Qw8^E<$O|Bx5ambF?5(sko0z_G8bs@v;?iv;!{ez1laP8_4aK>Z`Uw-xODT- z9gY@g=cw`oKU6QrlD~boOyla(?a2X`;6S&s-_`4d%Ft$v?9-?HeQ{kjXDace?fy!JkuHuugOFj+Mwip|~)yxu&I zWPDvIXzt%}{xfQ0YC*c-Gle&&%~y#}&uzvHq;_f(VfCR4mLBuqfWco^?3f>eqmq5y zHt)y3J>^o_t?q^SDMS!^dOPgI-vVCp%|E63V2Txw&b~GX%@^a_0@=dKH1TaPA7K%( zPIZK<$x;8zlHEVA-yA$lG>Z&ilQYOf-iIqw$Loxaq$a*%a%FW)nAuKVjXMMVwd;1e zIA0NZ5l4YaQi)b2SzYMd1_})p(ptn_PTS{y{rb7v@`Gj(7G_J8QbbPH^g1|;hTd}pPxHC9 zn}j`VoHgA2J})Wg!l@98&}n6!tHaO~wBp>^BVu0ge)p&93UF64gf?*87EumKOpHoD%qJm)+I0 z(>0Od89KteYbZOBw^pw4JfTMR;cV}-LpcR+SzV_EaZk{fwz1X?56{E}N3H+FDASpa z`3aX|*E9PAo~ydcJF6mqB!TL)tQ{Rju3jM@Gf2(pl15m!7RU>fFSS4%i2mSgf}k1^ zLyD;{bDZka?cgqiaLp}q(S=YvP*G|FILCO{(aV5QzMMfm>&m#uMryVHi@Wx!;+WuA{Fn~o z_>7t1oL(*AW?3(yFF-f@2R_vHgX=|X7mIqm*ghQsoJBuTt3oGRGfXT%bKyT99v|nv zsYaM}6Xr$vY=n}gvg-HN1ZF+RTFO$Q=LPK>2h%iW%jOM;bi}JPZgw{zYW8S)#}2b! zcqHauN~vBfwfZ^;F33u)Oa@+chj-Buv*KSk1VBVF?gb8;!Yrx7pcr~_qR*o&w{olR zy^Uy4sC>~QY}e=2LkB9M+0T=nO*GK4l%kzg(mg()_LV3OYK?!$>om7oi$J$>1%_r9 zA?s@O*3%K)Q6j^Bq_4&P&`{#tDHb;0(PYY1KHq1s?Sh!ysTbQcD^?^Mki6;sAL7B zL67OL#$HrwH*`5B0c~If&)O#p3I{h2C>q~n^>&f9;aU6Mc^+1u6!*gE4Snmst$Qz- zG>!FMg@=kDWs9{5CmNlt_<~nXW3L(<Y%a-tq>`l0>xfMLy>@e@&EgJ4%=w_=a0EJKyy5UX=YHv3u1H1#G~y5>v} z=uCL2DzK&5`VBF*31}*n+WtqA5Y^8720!&H2*3ol+B5`C)vS?opgG1?)qx$I+k~r8s^5D(cd>l@3D;bI%dw^r=ROOg&Za4+` z*uV}+g~Q-pu*G-Y+&F}uSuR@Wh2!DrN;~@vtT<&+BZWCz35VT5x<0v!IAz=J)5!1t dot2e4U~v!uq4)z!P2BGV0Is5?T&-vk@_&(d(I5Z- diff --git a/BTPanel/static/img/soft_ico/ico-idown.png b/BTPanel/static/img/soft_ico/ico-idown.png deleted file mode 100644 index e88d9e8b6e7cdfd882e135bf5a9da7e5814fddb6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21506 zcmeIw{ZEru6bJBI)Ii$+g;79Cf(UNRS%#()mefH>YfGgatHo&IODPiH2t#27vjhV| zm9*eQi&LnGg2Pe~a72YcqzfY(C{@u}VXIgtLFxwcWw^oEZMOIi?APy;^W1yRx##}y zeDaj1DW$%wFcu-iH$^5^(LKiV`YfPp*Am$_x-HxxQiup?JHnpLSVU_^o=TcbT5g6< z(BfEHs#=n|YcH`rBh8clh2<%+x(|GjPKqxR%LKVFF;6Gysn$R;%1Ps6l4&HCr?jcR z?M0g1p3_VNO~`JO{+1UFqB-oDw7Na(_6l0kre}kTyd#=A}9=!5z`KH5&`}A!( zzcOE?Tu(wo>X?G~g+9I-?`&a+{JkY%fyX#?WrgwTtZUZM%1SO*_o_ z`Te^;(DLbb{+c2dsS^h79+&DG;y6iV`@}xtN^{=GABkqkP|le(TL-^;NtiS0STduq zxe}hZJ6y)@zN)q^U6;1D^m@=*j&!nivHpi>kzbnX&`KSrrT!b<;2v)B((x(d(t#5i zVbCRRrp$V-FnHryt;EF+dcYcQJQKh@F6-2G&l$hvR);4k1id~1A2THDMt)o!st;Sw z4_&@&dZR4lXrnw&ag}@Z#h9s~m+99k4u0RYi|x&LzUG&+MiS+hyKUbUV=eDsDBr(y zX9QEpi7q7>kCr(q%d5-w_V~-3QT^q4=N}nVH9r;IFJQ*-+56uaTYUS=iaXA}7R%X~ z&icT)@M1ngQx#{|u}wnJ;gb{gp%2?WDyh1EjXh{vS0C{3PGOWv?%cx@v95nzSN$%_ z;n$gyp4Im1@{ves-_KS-K)ZUUO);_|Juo7B)w5SF`CG;I4*Tcs(J>v*u%%#bv}5*0 zQtlS5Jfq1j0yv^(^7U%vK!&@R0ow$Z;K#nzolf8>B)-GrrJ46MEs0b!{3qi#MT$9>2E_)Bua5h zvZln7KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(`>RI+y?e7jKeZ#YO-C z2eL^-K~#9!?3rzh9aR~}f9IT;xij}|-{@{%=~7yDTM8{DHrP@DvBro{6M^uO0wIkW zP0$$m(hmeb5)&bz`a%34HYhPEibX78Jj2N7z%aZQ$G zt%`StWA+y{he|OgN}7jPWm%R}EX+GR@xd5Fp<&%@mmB7#SkvY5%z-KozA^%iYEqPD ze8d8w;m0?&aDPv5#!{p$g}P;SaBk~e_ref6kJKQqE?6tgX(YDzzU1!3{=Y4`N-#S( zKjUxr#A&mf5!14eb_9_DPeDyv93?Ooj+8Z*=gu^j;hLqttZ^0W8;QBL%VV}LsainJ zq>s3Qp|WPraLnpXmr)H*?H=Rp!y${heZJJ4V)M=tF3_G+JiIc?W4p`PilOF7{xA@+ zsV7Zn05y|QOIz}uAl(q+5>IgDEQhNv$#KJi6!*L|%xn8X2qaj@DZ$p8TPfEKZ=BMk ztff0E*)tM@BWDnZvE)*cR|aC1centopXagjWW@b14YU1F73O#eZ;WbQJQ?xo;Tn#F zTQ5xku%{3+RE(K8jTs?CU(!FU`F>x8N^B=6RgodMI1^`bAzSbMyx)K#z7#Dg>Q>z&2g_yEUKo5q7CqE1! zBb##c=}A!>!ALRY>AjVSsT-d!@aXC+Cm!hHzNKjxhDuXx$_}G~+1>NxJci{yM$;L*@mN!7;BRv}AWt^VHf5KU$e(XJ3U!24hG`s3$%|MkSfBCz#G5o6OzH24Qx z5;zJ%W9b{w_>tw#Ph`$i zpKHVu3=Bs6U~kCQwRvtIi1_-N9A93VKCkU>pQz&*=)3C@o;_4$+wQTZLUHrq_f*90 z`?^@sF|{-Kdr7k}p9F>_&kS;4Ohfv@fiy>{0th6xy*R{hXr{DvVO}v%F|7RAAO}X` zB)ZLrkBko`pcs=6B==q(ux_r$@>Yk1IYs^~Tu}%Oe<{TL@^F- zI!i$%$#x4>OJOtu+aSKGNm@R6Cwbm9-s#!Xseaui&qOAPVA>{K8r!I=1N9#@C(r2v zHsK6xQXFBE@;YT zk$`Fs1fGOtjUr77L0$=F`I0#qb^6&UYb3U$9l?ghKAU>e{PuW_$M;ll5p2D_h530& zXG(JAwgO{SjWY1Z&$aQ*cgh?Z3_%I*?Fm>AC>FFSI$X(J+Y3B;eG6A)C7B9xP6{Ss4W>!@R{d_l9E`6DQd((M!@>n9-p4) zC2gA+dRr7_Yw53Av=YRwfDz5Di+nC^b2vGwp<>8+k|(aqvvFC#;*4VFU>$tP4Rbvr z0mmwuMW~4_4XI7eZ|5&MIEmA5ti4j#o_Llk!4LE3z!^bg2Rlygh= z{P%A<=PkfwP+VX^B+il*oYiw1DZOlLGUxv0^!@1h*!;1&2;?G=i$MOb$3F)EPSYdX T5h3G}00000NkvXXu0mjfzQFX$$MuFfEks%J0(jX%0hRR4mP(ujf3;llk+<&o?oO_a!o12@Pb7gVt?<}y} zun+`Uu$c4&L_-m}qcdFI&5Tmem2 zy1KfwS}jm2H5!dtt!{5`2WM-6LebLF(%jq(SDw0b>C(lE7ePe@Tvh_g%0X$FQmF(5 zg`lvowzjsWrUr;5)z#Gsg`%pe3J3(1m6d?cFE1|#nHeP|B}GL=fXjuG_7xTu78DeK z_ymxU2-s|qNCaZpLZJ|jj>*f*19Uo|(Q&4+jROr>BDe3LumB@81uT zNU5o*TrL-QdL}0)1NU7V4hOin0cYp9xHyyu3U- zJe-}Kot&H;9UTb-g1x=Hot>Snt*woXjg^&^g@uK=xj7b#MUr4@YKlUkkZ7Eguzy12 znk0dp2q9Slnq53%+JgwMhQgO1^J@GVq#<><;9IIIr1ubEP4co zDa%T?M@hEGZHq6rDwFEBccf4$=q;tg751-P%1WCEfp$@y^j`;i{9Dub=dCh2`5+)B zS!-eu7l6>+YP~tavGdF|Z};pjaxH zmn-!x#&wyOoL$xS5MTFh;y`F5CRQuF?DfFFzPBp`!$Lsz!?I#zPYk?QZk}%5cSm1z zpYV*MxnxazI>s5u9O> z(DRnfGpO1N*!oe`fuZV-6dl7dMTa_j&V;Ai$`RlHv@PBh)j{=;({s`+CmSh)jd)gx zwA!_6L1y|sZ$X=SrpncjXjk@}6tp?A0dN1vX72^nX?q9dr$dIXBJbx-?^JY1SKq*j zi3!=)v=^%LY~NFgZ&cK_eU4*))>+HlRfon^-yVPX_tAKNeR$%c4=3)WrH!r9_m?=iVKs(2d= zTOS;MGWF@y{hIZEX5_AOKQ|jjbI6Peg5IR(R&nOxQNCNGZpD>BT=I+E%J3DcrrS^u zYxO39pBWJ1TPfh{k1m=l>l<1}Pidr&>9%@ueNe=aw6Mo$zR=O7`KG}i7iHEBt?|e> zDkjAzbsm)=)hKawqz1}mFT?Flwa>F8t0Y}olFHNO09Cz7HCAkM*Q%a*;s*sr%@xV0 z+f_Pc-=mrshnFTXa(OQ#K`Ny^_+cT!niHMom8^_n2kbFWc2Ai&-rI-E- DP%_q| diff --git a/BTPanel/static/img/soft_ico/ico-qiniu.png b/BTPanel/static/img/soft_ico/ico-qiniu.png deleted file mode 100644 index 3c5d262ae58c306cbdf4089db68201e26bb47798..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 630 zcmeAS@N?(olHy`uVBq!ia0vp^RzR%5!VDy1yT5b;Ddu7)&kzm{j@u9Y9{{-q0X`wF z|NsA&?EFPMsyAN{3e+o5666=mAgE;Q5}H=tF>}@KGk4$pFPq9J3Y6n4@Q5sCVBmTU z!i-Jbyiq_wnG)BClHmNblJdl&R0hYC{G?O`x6Go{^8BLgVg=`5vln|d^)N6n&h&I~ z46*2~oxIWeu!G34c+;p{S*sl!T1z8jT9h6w@-8VZI3cfa&GE=uKE6Cw_VCiul?ynm zu5hgjnsdali|bKXNX+bmVQX_gRqj;DEt>vKTJQa*-FNm*|1KLCKDRV+`IPzZIN0}j z)V}f9%-Us=ESq3&5>#-zF^$JeI*SGChb*rZ z$31d6G++LB=*U&EaLP;Xz1ultKCj?E@Kxj59s~9rQ`02s3Z@nDKEFA8dDzWqb}?d6 zTANo2mOo!DX8L}6x=`nlH_v$EYp&WYV~L*Xb+q%ik|<`C@(DQFs3q zdH1De+3&h0{4H_bUcNVafm{;Iq!9a&Son4KEBR-c$Z74$=w3^!imcgtlotr zcs=Jk_%=v{x6|$5+(YMAc$WxNY+fw0PxaKTjA-o;x6nmgb9iTl-c!AGSnBX@F~%pS z51Vb#wYukZUi;Z0-Pb2$)GX(}5c-jFZswJ_p>?UZba{4MO4j|M?i^Gyoh|NWCHsE1 k`0r2JmVT{J;JILLy;#;LW&dU$U}Q3Qy85}Sb4q9e0M`}_7ytkO diff --git a/BTPanel/static/img/soft_ico/ico-safelogin.png b/BTPanel/static/img/soft_ico/ico-safelogin.png deleted file mode 100644 index 379c72b0a3df520937fb0131b7b639631fd3adf3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 715 zcmeAS@N?(olHy`uVBq!ia0vp^MnJ5=!3-q3*F@Fg<0o8LBctjR6 zFmMZjFyp1Wb$@_@G9|7NCBgY=CFO}lsSJ)O`AMk?Zka`?<@rU~#R|^BW-s<^>S17D zoagD{7-G?TcIw-(W&@tKi+O%)jU3dw0P-;Ux$lp&P#4E;m^EvAth2l zarQL{6(y6oT1z=(^Y*%4jpAxqw$|JtaK%Ac!2?-qbs3WKoAjEd-kU$yx7xD**zq~) zCl?$qIU!Lu^{TI6XS~VW(ukc-1}3^+Zwc0}5xAkaY0a_!jWQyOtp2b~oYteLeOZw4 zz&#-c6=kmC^3#D!m7H$>J9y5zj^8lNVAg(TKQ4#LkAJ7MYm!!3Zd_E)cP(jLfmPWjyyUGn4pZgPXj?^q&X{EGz z+4+v2jAs2t_o{^7b%mc~e`9IYu+e9ai2SBd6StI|Y_-P0?QTbpd>4DD)w`iODk{#R zcvqfq%!W4!;*BC7ypy`O2Ar#9`&#^ed6BRC>Ds2#t4d=}zuk3Z4y)Pkvqgd&Tk<#F ty({3nfbEx9=I1E_d;9n{P5<$L-(JNo`&)=OA23E4JYD@<);T3K0RaAqAASG; diff --git a/BTPanel/static/img/soft_ico/ico-ss.png b/BTPanel/static/img/soft_ico/ico-ss.png deleted file mode 100644 index 9b6bff7c830e0998f1a1fa3aafb0a59350533ee1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3335 zcmV+i4fyhjP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ|jY&j7RCwCdm}_uV*LBDLYwvwt_o4R#ArOeSdDSw0QXY12vFkEq%;aG=$z-qr zw{fR+GH%-GOgn9z>16VlerP7`w4K)N*m2{yb>rBM!7mJf@-i3!0wf^;S4be~B3<3r zx#yg{S3gK#j3gV3ox114{dCVh>$lfh|NUQ^p5FR8Qe1~Mcg_JD;Cz1pOXk*~{p<)F zhmb8qkV>MIE1S6!U9(Y~?i)K~eNM{;ggXYF+xN)GpV-Srs zaTG#`x&7ha zobk=iJO~ls2m%>K-Vd9f+u6DC?ZX3)pUvwkpDnc1ct>w;uKdMk6NCW)f+GYpLmuiF zxc9YpPd#$zY+-$0R=AEuAjg4I1X^a-y==M8sm~S&0ZJ02A`nN_+YhB4{GYQ!Pj;rm z`6VOZDi1MNgDC=Q3rtzeZ!FtYUt0(DSpje)!D2&0!6Dj&&9CJNC(QY z5H~am00LMcVnSV<^K+Uij!2vSZ$T6Ru9C0?sl#1^zty_4`ww@v_1{0taX3U!wtKnM z42=OmWuk*8VTOb-aJ$v2<}Bvw@=$h!Man zM+qicgAEh{toe;O&oh9Ce~lnSfGY(QF^=~Pwfu7bnLpamKJ>+Okz=GJ9A$%vAXozc zG)pK!VCD(5fROOqV0xW;>tZpBNSjX|L`i}N2^dqc?_}?~AN}I|WACN%D@L>p%d+7* zdbI8V2sY5HfG9w1xr2^4d<(DLw#is}25W!P~!J)#m16@O3-+G{D zQ`d-|83@3WF~kJ}7=>UE2tc3#3jyv~ppeQ*4CX=zF@EhphiGZ8*~XSr%9om9eg;I2^vDOVA~!n2uO}#aHVu!!chpL2yxExF!*MA3=D+OSwW~e1JQ4FIJv%^vHiU9d6>ptC{iTYEB(ydk4Z>p? za4dq9gx-`KJtSE1A;^V0}0;`aN88S^UL=w+;Y#thPPpWp<e+oSz0*;=+x9#V z>#G!P;wLf&GwTv(=1#4Dqf}s+tF&3MG4WI92OfIoSpVa#eL+hB4lI>`g^BTAi)~-aoT^TcLaVAjgR5YqeoED zI1AZK4%-hNUmz={eeUq-p$EEpGq?3+!#dw6C?%mJfrzfi(dDxl#1fGqgi#5%HaR=K zy1a4Ij4AaU#R!-(ErDXD=>FjOgD-6BEdEZ?l`xQ_*5q2V!9YTU_`An$Th>@tb})>_ zXHqdUiU#lvBj(yC?piYK#HyR7{CF@uT$wBB+uP0!Z`jwJx$E>`VaZ5IKnMvTC8QY5 zkRgep1{zep@P*a0zc-`GdMX+BjP2Q|N!U0ykWc*RKM(zEM|bi5gll~)2N7yUbG@@y zkvZ6NVR&MfeWmsq2Ksd55RH7njqg2w~-@kO^#T{M#y)n-M127;6n9J7v zqe_Zkz&gaj=@sua)g^Gr?M@Cd?)+f{5hJew4Kmf`$zR<(ch;|F)i~cda<;!|cWY`@ z?@+WZ;qu_-^|$<)<=P`biE-CW(@+@`7}2c0``o_&di!Ml-k58Fujq(tfw=TvLjn#N zRCv}qC(dS2y26%R6Uf!ae8dc2GYkWsZJk{@qvn|hZmoF6kU`S3F_;bD4-7znFv2XK z+0p*Oe;+B{uk08iHV`Do!oT+35Qq?J8_ngEUOIpJ+gd6}#Ww(Q)jVJT1k5N#4A7{% zO7{;S)z%3i1-zIeo%+Tf{;o>z%16BN_<8@%3zpj;!q-2xg>F`OE+&$Rm)9?z{_>ZC;7?xPeX5$2zN5E5EB2i@ z_u%;)&lxUR$~O!t1(XyZ0t9O$9iSoM?&-NuK%_s(jL>M68^cgaqOWfdU8l}LNDHJ0 zAb?mT+ET+$ly&qTo?2c0Ry+~UpYG2fs{w}u9LG-2YAk~-UjmpOdVZfFG)nzVbOif~1LlVncnsI4< zk_3$8gV@)8u=`stwjZmUQAq=HYZGtQmfNq~G_!X5oJy}}<&s8Zg8=6X28O9rm5g<@ zG*@@6ZJG9Le?D+J&karK?oQp=em+{ethsVmDO)g;snzv?5McDEQ5(sJUbo31)j0}jbQ`iX1b~PaqcyhzS#}?16-<>NIKj523FWLmWxI#AT-#7>Z z5C9xop~M0|*?A1_w)X;H)K?`w)}n?%-Tgsq}v6 zfza1_7*QBN33=URG~xUZM(IcHxqWkW+|HB?CchhDpxTkJ>te_TH%9Wm0{~y&>xEAq R814W7002ovPDHLkV1nG}H--QJ diff --git a/BTPanel/static/img/soft_ico/ico-yunsuo.png b/BTPanel/static/img/soft_ico/ico-yunsuo.png deleted file mode 100644 index 0f199469db181c3db67b721e07f5f629429c92d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2164 zcmds1`#TegAD&C6k;+aNl4?^59l0cwa;GLFw$7oeTdpCblysvL7GrXfA<1={%j_Dn zWMgIZ50AMThoTKLkn{Gf4 zsI;-uu-=Iqq!eQhg#m%UZ-W;G002d}hbzoUE|)LK<l-pJpcd zB%|X)BU6JT0?BCOU!7QxKO*LI^RfM+g5karu`qL}m;Je`uKO#I-^t-{(7oMs zUVFNT|6If;@z^=t+&FPpODhY*Wfym_X|2qs0?ykG_8lRIz+?uBc-1Y;TfMwgUfa7C zW@2XxoksWR;gD!FY$J_Hp~P@o3Y%zwpIZ`GA20G5Br@5%quHjLQAsAB?_!`DKcs%7 zoZ>g-lL^lnDSj;aIUe1ngC5pIwd|y2k?R~d6j*CLf=u*bP!YsxXe-UAqXE)Yf2W@4 z#UP<-stmXkI|c>9tB3H2p;WvPn{3rgEGnzGUsHLUL5w0`wc3gPMBGs(AsJJah$#cN z5R=PGAxvCqNpaTuw=hEGQF@gt38Tp**i*5Z3>=7ozlnLT-i*6b_U>Q<&ISM82V1P! zh%=y+-!ICusw+{a;Z*2ld+JMJ6>n{;i^1gLpBNeNVOTzMADUgackqXpn^J7t2doX#H@3NiqGvWWeQpM(N z$K)6kp)`tLsg=A|FV2F!Oam1lv~ttUktxSAk~SPwys!|t;kVXUPj|14^Z#G~3N}i= zBcu{_&N~JG*oOWMh0WPseH%~hSf_tty&`YM-t~c`UQfWL!txkMn;FlfE~Qd z#~bj74c+eFI)~^$-`J4I2rw+J=CKsgucXa}==dFNGz&E6pG z)&l{#st}8`^Exf=#5D7Lf8wsgZ@FG}U1K3?mO&-qC!C&L)Q-bf#3<8#jQUt1JU@4r zCL+8iq`fg3Hd)PMul~LkKRVwaUHNsoe{4ltXyq}qoy+A;m9cJ~d_YmxUM-)tbW?`f zX72K1JTD|@t*7i*(Rw)k5|##3B*SJVtfHa|!B9MJ2Xv=R0XE%uHaW#1^L@U{Pg{oF z%yLZPZ0xsjkezTUu9Bw|q5;V{cff|a9BpWaJe!oS<9=3ENdXDgt2B~<^-^`tASDOS zQ@A(N)Uq6uO>Sts;N7=QwzbqAQGo+jebWt!tR)y%eOsjXP(JNYCBs?eqdxGOrlRe+ zv93nHM~{;%xQG}FCFGXT27Q+R?AqfQ1rLERXzZou`GqHKzC8?-68a z{A+K2JNINd;*v5uORYh7U~ks(t3S%}Mj~2+eYJPCvsE+djw65&=dh08=<8y1zIZWq60>e>FaOM8+;-uulH zem#B$cLds5A2?8a%K)$%)@^PDPh`1;2H`AJFEVy?%^g2)_7JOT=YgqO=A{|8mw_^8 z%G>y|5NE@u2B-&zFIn7B>BXpO{}W9(1~IEC+c~)G zq78puG{^*Nxhg>dKYN;#UhMaGY&kXXGxMtNDNN%6y+Z9-QvmV_7+cQC4lGy}=B!d| zE$}R3G)wbI9h|)yna~(TbtH)@rxouK}4C$o?fk;a6CS^G^0P2pjgGgqjNysJ7e1Lq2U9 zs!3vI^U^1j9b-~#*sPK#i}ZlQS0A@U9UHtpdWR@7 z{&Z064fJ*2cY*L)0S%Q-LmNIC#40M=*|V`95%jFT=%!12=*HvUZY6 zk7Tfy*4-O+H{HKJvfJTrKmXOW!AThW80Ev_uoCskHmejpPCKSg|LU>*xihu~OM4md zhM`Gut802%_E`X@*zj12=bpWtE;2YYHO*q`uh4d+2s>hc^k7aM&o3H zY~HAm2I~4_EU#!E0t_*ph+x%aCkX1R&O`=Ocf~V>2f=1?E=4fw7IaeZ2{#d$x4>Xs p^Kg4WNWq;%8{{V`z*QEde diff --git a/BTPanel/static/img/soft_ico/ico_idown.png b/BTPanel/static/img/soft_ico/ico_idown.png deleted file mode 100644 index e88d9e8b6e7cdfd882e135bf5a9da7e5814fddb6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21506 zcmeIw{ZEru6bJBI)Ii$+g;79Cf(UNRS%#()mefH>YfGgatHo&IODPiH2t#27vjhV| zm9*eQi&LnGg2Pe~a72YcqzfY(C{@u}VXIgtLFxwcWw^oEZMOIi?APy;^W1yRx##}y zeDaj1DW$%wFcu-iH$^5^(LKiV`YfPp*Am$_x-HxxQiup?JHnpLSVU_^o=TcbT5g6< z(BfEHs#=n|YcH`rBh8clh2<%+x(|GjPKqxR%LKVFF;6Gysn$R;%1Ps6l4&HCr?jcR z?M0g1p3_VNO~`JO{+1UFqB-oDw7Na(_6l0kre}kTyd#=A}9=!5z`KH5&`}A!( zzcOE?Tu(wo>X?G~g+9I-?`&a+{JkY%fyX#?WrgwTtZUZM%1SO*_o_ z`Te^;(DLbb{+c2dsS^h79+&DG;y6iV`@}xtN^{=GABkqkP|le(TL-^;NtiS0STduq zxe}hZJ6y)@zN)q^U6;1D^m@=*j&!nivHpi>kzbnX&`KSrrT!b<;2v)B((x(d(t#5i zVbCRRrp$V-FnHryt;EF+dcYcQJQKh@F6-2G&l$hvR);4k1id~1A2THDMt)o!st;Sw z4_&@&dZR4lXrnw&ag}@Z#h9s~m+99k4u0RYi|x&LzUG&+MiS+hyKUbUV=eDsDBr(y zX9QEpi7q7>kCr(q%d5-w_V~-3QT^q4=N}nVH9r;IFJQ*-+56uaTYUS=iaXA}7R%X~ z&icT)@M1ngQx#{|u}wnJ;gb{gp%2?WDyh1EjXh{vS0C{3PGOWv?%cx@v95nzSN$%_ z;n$gyp4Im1@{ves-_KS-K)ZUUO);_|Juo7B)w5SF`CG;I4*Tcs(J>v*u%%#bv}5*0 zQtlS5Jfq1j0yv^(^7U%vK!&@R0ow$Z;K#nzolf8>B)-GrrJ46MEs0b!{3qi#MT$9>2E_)Bua5h zvZln7 -

目标URL

-

发送域名

-

内容替换

-
-
    -
  • 目标Url必需是可以访问的,否则将直接502
  • -
  • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
  • -
  • 若您是被动代理,请在发送域名处填写上目标站点的域名
  • -
  • 若您不需要内容替换功能,请直接留空
  • -
  • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
  • -
- \ No newline at end of file diff --git a/BTPanel/static/js/config.js b/BTPanel/static/js/config.js index 6fb9f901..4fe2a097 100644 --- a/BTPanel/static/js/config.js +++ b/BTPanel/static/js/config.js @@ -377,15 +377,15 @@ function bindBTName(a,type){ if(a == 1) { p1 = $("#p1").val(); p2 = $("#p2").val(); - var loadT = layer.msg(lan.config.token_get,{icon:16,time:0,shade: [0.3, '#000']}); + var loadT = layer.msg(lan.config.token_get, {icon: 16, time: 0, shade: [0.3, '#000']}); $.post(" /ssl?action=GetToken", { username: p1, password: p2 - }, function(b){ - bt.clear_cookie('bt_user_info') + }, function (b) { + bt.clear_cookie('bt_user_info'); layer.close(loadT); - layer.msg(b.msg, {icon: b.status?1:2}); - if(b.status) { + layer.msg(b.msg, {icon: b.status ? 1 : 2}); + if (b.status) { window.location.reload(); $("input[name='btusername']").val(p1); } @@ -448,224 +448,232 @@ function setPanelSSL(){ var status = $("#panelSSL").prop("checked"); var loadT = layer.msg(lan.config.ssl_msg,{icon:16,time:0,shade: [0.3, '#000']}); if(status){ - var confirm = layer.confirm('Whether to close the panel SSL certificate', {title:'Tips',btn: ['Confirm','Cancel'],icon:0,closeBtn:2}, function() { + var confirm = layer.confirm('Whether to close the panel SSL certificate', { + title: 'Tips', + btn: ['Confirm', 'Cancel'], + icon: 0, + closeBtn: 2, + cancel: function () { + $("#panelSSL").prop("checked", true); + } + }, function () { bt.send('SetPanelSSL', 'config/SetPanelSSL', {}, function (rdata) { layer.close(loadT); if (rdata.status) { - layer.msg(rdata.msg,{icon:1}); + layer.msg(rdata.msg, {icon: 1}); $.get('/system?action=ReWeb', function () { }); setTimeout(function () { window.location.href = ((window.location.protocol.indexOf('https') != -1) ? 'http://' : 'https://') + window.location.host + window.location.pathname; }, 1500); - } - else { - layer.msg(res.rdata,{icon:2}); + } else { + layer.msg(res.rdata, {icon: 2}); } }); return; - }) - } + }, function () { + this.cancel(); + }); + } else { - bt.send('get_cert_source', 'config/get_cert_source', {}, function (rdata) { - layer.close(loadT); - var sdata = rdata; - var _data = { - title: 'Panel SSL', - area: '630px', - class: 'ssl_cert_from ssl_cert_panel_from', - list: [ - { - html: '\ -
\ - \ -

' + lan.config.ssl_open_ps + '

\ -
    \ -
  • ' + lan.config.ssl_open_ps_1 + '
  • \ -
  • ' + lan.config.ssl_open_ps_2 + '
  • \ -
  • If panel is not accessible, you can click the link below to find solutions
  • \ -
\ -
\ - ' - }, - { - title: 'Cert Type', - name: 'cert_type', - type: 'select', - width: '260px', - // value: sdata.cert_type, - value: '3', - items: [ - {value: '1', title: 'Self-signed certificate'}, - {value: '2', title: 'Let\'s Encrypt'}, - {value: '3', title: 'I have certficate'} - ], - callback: function (obj) { - var set_height = function () { - var layer_box = $('.ssl_cert_from').parents('.layui-layer'); - var window_height = $(window).height(); - var height = layer_box.height(); - var top = (window_height - height) / 2; - layer_box.css({ - 'top': top + 'px' - }); - } - var subid = obj.attr('name') + '_subid'; - var keyid = obj.attr('name') + '_keyid'; - $('#' + subid).remove(); - $('#' + keyid).remove(); - if (obj.val() == '1') { - set_height(); - } - if (obj.val() == '2') { - var _tr = bt.render_form_line({ - title: 'E-Mail', - name: 'email', - width: '260px', - placeholder: 'Admin E-Mail', - value: sdata.email - }); - obj.parents('div.line').append('
' + _tr.html + '
'); - set_height(); - } - if (obj.val() == '3') { - var loadT = layer.msg(lan.config.get_cert, {icon: 16, time: 0, shade: [0.3, '#000']}); - $.post('/config?action=GetPanelSSL', {}, function (cert) { - layer.close(loadT); - if (cert.privateKey === 'false') { - cert.privateKey = 'paste your Private key (KEY) here'; - } - if (cert.certPem === 'false') { - cert.certPem = 'paste your Certificate (CRT/PEM) here'; - } - obj.parents('div.line').append('\ -
\ -
Key
\ - \ -
\ -
Certificate (in pem format)
\ - \ -
\ -
\ -
\ - '); - set_height(); - }); - } - $('.ssl_cert_from .line .tname').css('width', '75px'); - } - }, - { - html: '\ -
\ - \ - \ - ' + lan.config.ssl_open_ps_5 + '\ -
\ - ' - } + bt.send('get_cert_source', 'config/get_cert_source', {}, function (rdata) { + layer.close(loadT); + var sdata = rdata; + var _data = { + title: 'Panel SSL', + area: '630px', + class: 'ssl_cert_from ssl_cert_panel_from', + list: [ + { + html: '\ +
\ + \ +

' + lan.config.ssl_open_ps + '

\ +
    \ +
  • ' + lan.config.ssl_open_ps_1 + '
  • \ +
  • ' + lan.config.ssl_open_ps_2 + '
  • \ +
  • If panel is not accessible, you can click the link below to find solutions
  • \ +
\ +
\ + ' + }, + { + title: 'Cert Type', + name: 'cert_type', + type: 'select', + width: '260px', + value: sdata.cert_type, + items: [ + {value: '1', title: 'Self-signed certificate'}, + {value: '2', title: 'Let\'s Encrypt'}, + {value: '3', title: 'I have certficate'} + ], + callback: function (obj) { + var set_height = function () { + var layer_box = $('.ssl_cert_from').parents('.layui-layer'); + var window_height = $(window).height(); + var height = layer_box.height(); + var top = (window_height - height) / 2; + layer_box.css({ + 'top': top + 'px' + }); + } + var subid = obj.attr('name') + '_subid'; + var keyid = obj.attr('name') + '_keyid'; + $('#' + subid).remove(); + $('#' + keyid).remove(); + if (obj.val() == '1') { + set_height(); + } + if (obj.val() == '2') { + var _tr = bt.render_form_line({ + title: 'E-Mail', + name: 'email', + width: '260px', + placeholder: 'Admin E-Mail', + value: sdata.email + }); + obj.parents('div.line').append('
' + _tr.html + '
'); + set_height(); + } + if (obj.val() == '3') { + var loadT = layer.msg(lan.config.get_cert, {icon: 16, time: 0, shade: [0.3, '#000']}); + $.post('/config?action=GetPanelSSL', {}, function (cert) { + layer.close(loadT); + if (cert.privateKey === 'false') { + cert.privateKey = 'paste your Private key (KEY) here'; + } + if (cert.certPem === 'false') { + cert.certPem = 'paste your Certificate (CRT/PEM) here'; + } + obj.parents('div.line').append('\ +
\ +
Key
\ + \ +
\ +
Certificate (in pem format)
\ + \ +
\ +
\ +
\ + '); + set_height(); + }); + } - ], - btns: [ - { - title: 'Close', name: 'close', callback: function (rdata, load, callback) { - load.close(); - $("#panelSSL").prop("checked", false); - } - }, - { - title: 'Submit', - name: 'submit', - css: 'btn-success', - callback: function (rdata, load, callback) { - if (!$('#checkSSL').is(':checked')) return bt.msg({ - status: false, - msg: 'Please confirm the risk first!' - }); - layer.confirm('Whether to open the panel SSL certificate', { - title: 'Tips', - btn: ['Confirm', 'Cancel'], - icon: 0, - closeBtn: 2 - }, function () { - var loading = bt.load(); - var type = $('select[name="cert_type"]').val(); - if (type == '3') { - SavePanelSSL({ - loading: false, - callback: function (res) { - SetPanelSSL(rdata, function (res) { - loading.close(); - }); - } - }); - } else { - SetPanelSSL(rdata, function (rdata) { - loading.close(); - }); - } - }); - } + } + }, + { + html: '\ +
\ + \ + \ + ' + lan.config.ssl_open_ps_5 + '\ +
\ + ' + } - } - ], - end: function () { - $("#panelSSL").prop("checked", false); - } - }; - var _bs = bt.render_form(_data); - setTimeout(function () { - $('.cert_type' + _bs).trigger('change') - }, 200); - }); - } + ], + btns: [ + { + title: 'Close', name: 'close', callback: function (rdata, load, callback) { + load.close(); + $("#panelSSL").prop("checked", false); + } + }, + { + title: 'Submit', + name: 'submit', + css: 'btn-success', + callback: function (rdata, load, callback) { + if (!$('#checkSSL').is(':checked')) return bt.msg({ + status: false, + msg: 'Please confirm the risk first!' + }); + layer.confirm('Whether to open the panel SSL certificate', { + title: 'Tips', + btn: ['Confirm', 'Cancel'], + icon: 0, + closeBtn: 2 + }, function () { + var loading = bt.load(); + var type = $('select[name="cert_type"]').val(); + if (type == '3') { + SavePanelSSL({ + loading: false, + callback: function (res) { + SetPanelSSL(rdata, function (res) { + loading.close(); + }); + } + }); + } else { + SetPanelSSL(rdata, function (rdata) { + loading.close(); + }); + } + }); + } + + } + ], + end: function () { + $("#panelSSL").prop("checked", false); + } + }; + var _bs = bt.render_form(_data); + setTimeout(function () { + $('.cert_type' + _bs).trigger('change') + }, 200); + }); + } } function SetPanelSSL(rdata, callback) { - bt.send('SetPanelSSL', 'config/SetPanelSSL', rdata, function (rdata) { - if (callback) callback(rdata); - if (rdata.status) { - $.get('/system?action=ReWeb'); - layer.msg(rdata.msg, {icon: 1, time: 1500}, function () { - window.location.href = ((window.location.protocol.indexOf('https') != -1) ? 'http://' : 'https://') + window.location.host + window.location.pathname; - }); - } else { - layer.msg(rdata.msg, {icon: 2}); - } - }); + bt.send('SetPanelSSL', 'config/SetPanelSSL', rdata, function (rdata) { + if (callback) callback(rdata); + if (rdata.status) { + $.get('/system?action=ReWeb'); + layer.msg(rdata.msg, {icon: 1, time: 1500}, function () { + window.location.href = ((window.location.protocol.indexOf('https') != -1) ? 'http://' : 'https://') + window.location.host + window.location.pathname; + }); + } else { + layer.msg(rdata.msg, {icon: 2}); + } + }); } -function GetPanelSSL(){ - var loadT = layer.msg(lan.config.get_cert,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config?action=GetPanelSSL',{},function(cert){ - layer.close(loadT); - var certBody = '
\ +function GetPanelSSL() { + var loadT = layer.msg(lan.config.get_cert, {icon: 16, time: 0, shade: [0.3, '#000']}); + $.post('/config?action=GetPanelSSL', {}, function (cert) { + layer.close(loadT); + var certBody = '
\
\ -
'+lan.config.key+'
\ - \ +
' + lan.config.key + '
\ + \
\ -
'+lan.config.pem_cert+'
\ - \ +
' + lan.config.pem_cert + '
\ + \
\
\ - \ + \
\
\ \
' layer.open({ type: 1, - area: "600px", - title: lan.config.custom_panel_cert, - closeBtn: 2, - shift: 5, - shadeClose: false, - content:certBody - }); - }); + area: "600px", + title: lan.config.custom_panel_cert, + closeBtn: 2, + shift: 5, + shadeClose: false, + content: certBody + }); + }); } // function SavePanelSSL(){ @@ -684,54 +692,55 @@ function GetPanelSSL(){ // } function SavePanelSSL(option) { - option = option || { - loading: true - }; - var privateKey = $("#key").val().trim(); - var certPem = $("#csr").val().trim(); - if (privateKey === 'false') return layer.msg('Please paste your Private key (KEY) here', {icon: 2}); - if (certPem === 'false') return layer.msg('Please paste your Certificate (CRT/PEM) here', {icon: 2}); - var data = { - privateKey: privateKey, - certPem: certPem - } - var loadT; - if (option.loading) { - loadT = layer.msg(lan.config.ssl_msg, {icon: 16, time: 0, shade: [0.3, '#000']}); - } - $.post('/config?action=SavePanelSSL', data, function (rdata) { - if (option.loading) layer.close(loadT); - if (rdata.status) { - if (option.callback) { - option.callback(rdata); - } else { - layer.closeAll(); - layer.msg(rdata.msg, {icon: 1}); - } - } else { - layer.msg(rdata.msg, {icon: 2}); - } - }); + option = option || { + loading: true + }; + var privateKey = $("#key").val().trim(); + var certPem = $("#csr").val().trim(); + if (privateKey === 'false') return layer.msg('Please paste your Private key (KEY) here', {icon: 2}); + if (certPem === 'false') return layer.msg('Please paste your Certificate (CRT/PEM) here', {icon: 2}); + var data = { + privateKey: privateKey, + certPem: certPem + } + var loadT; + if (option.loading) { + loadT = layer.msg(lan.config.ssl_msg, {icon: 16, time: 0, shade: [0.3, '#000']}); + } + $.post('/config?action=SavePanelSSL', data, function (rdata) { + if (option.loading) layer.close(loadT); + if (rdata.status) { + if (option.callback) { + option.callback(rdata); + } else { + layer.closeAll(); + layer.msg(rdata.msg, {icon: 1}); + } + } else { + layer.msg(rdata.msg, {icon: 2}); + } + }); } function SetDebug() { - var status_s = {false:'open',true:'close'} + var status_s = {false: 'open', true: 'close'} var debug_stat = $("#panelDebug").prop('checked'); bt.confirm({ - title: (debug_stat?'Open':'Close') + " developer mode", - msg: "Do you confirm to "+ (debug_stat?'open':'close') +" developer mode?", - cancel: function () { - $("#panelDebug").prop('checked',debug_stat); - }}, function () { - var loadT = layer.msg(lan.public.the, { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post('/config?action=set_debug', {}, function (rdata) { - layer.close(loadT); - if (rdata.status) layer.closeAll() - layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); - }); - },function () { - $("#panelDebug").prop('checked',debug_stat); - }); + title: (debug_stat ? 'Open' : 'Close') + " developer mode", + msg: "Do you confirm to " + (debug_stat ? 'open' : 'close') + " developer mode?", + cancel: function () { + $("#panelDebug").prop('checked', debug_stat); + } + }, function () { + var loadT = layer.msg(lan.public.the, {icon: 16, time: 0, shade: [0.3, '#000']}); + $.post('/config?action=set_debug', {}, function (rdata) { + layer.close(loadT); + if (rdata.status) layer.closeAll() + layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); + }); + }, function () { + $("#panelDebug").prop('checked', debug_stat); + }); } function set_local() { @@ -1237,11 +1246,11 @@ function modify_basic_auth() { m_html = '
' + '

Warning! Do not understand this feature, do not open!

' + '
    ' - + '
  • You must use and understand this feature to decide if you want to open it!
  • ' - + '
  • After opening, access the panel in any way, you will be asked to enter the BasicAuth username and password first.
  • ' - + '
  • After being turned on, it can effectively prevent the panel from being scanned and found, but it cannot replace the account password of the panel itself.
  • ' - + '
  • Please remember the BasicAuth password, but forget that you will not be able to access the panel.
  • ' - + '
  • If you forget your password, you can disable BasicAuth authentication by using the bt command in SSH.
  • ' + + '
  • '+lan.config.know_risk+'
  • ' + + '
  • '+lan.config.basic_auth_desc1+'
  • ' + + '
  • '+lan.config.basic_auth_desc2+'
  • ' + + '
  • '+lan.config.basic_auth_desc3+'
  • ' + + '
  • '+lan.config.basic_auth_desc4+'
  • ' + '
' + '
' + '' diff --git a/BTPanel/static/js/control.js b/BTPanel/static/js/control.js index 2f28703b..5adedabb 100644 --- a/BTPanel/static/js/control.js +++ b/BTPanel/static/js/control.js @@ -132,6 +132,9 @@ function SetControl(act){ } loadT = layer.msg(lan.public.the,{icon:16,time:0}) + if(!/^-?\d+$/.test(day)){ + return layer.msg('The number of days to keep must be an integer',{icon:2,time:2000}); + } $.post('/config?action=SetControl','type='+type+'&day='+day,function(rdata){ layer.close(loadT); layer.msg(rdata.msg,{icon:rdata.status?1:2}); @@ -218,7 +221,6 @@ $.get('/ajax?action=GetCpuIo&start='+b+'&end='+e,function(rdata){ yAxis: { type: 'value', name: lan.public.pre, - boundaryGap: [0, '100%'], min:0, max: 100, splitLine:{ @@ -266,7 +268,8 @@ $.get('/ajax?action=GetCpuIo&start='+b+'&end='+e,function(rdata){ } ] }; - myChartCpu.setOption(option); + myChartCpu.clear() + myChartCpu.setOption(option, true); window.addEventListener("resize",function(){ myChartCpu.resize(); }); @@ -307,7 +310,6 @@ $.get('/ajax?action=GetCpuIo&start='+b+'&end='+e,function(rdata){ yAxis: { type: 'value', name: lan.public.pre, - boundaryGap: [0, '100%'], min:0, max: 100, splitLine:{ @@ -355,7 +357,8 @@ $.get('/ajax?action=GetCpuIo&start='+b+'&end='+e,function(rdata){ } ] }; - myChartMen.setOption(option); + myChartMen.clear() + myChartMen.setOption(option, true); window.addEventListener("resize",function(){ myChartMen.resize(); }); @@ -364,109 +367,156 @@ $.get('/ajax?action=GetCpuIo&start='+b+'&end='+e,function(rdata){ //磁盘io function disk(b, e) { - $.get('/ajax?action=GetDiskIo&start=' + b + '&end=' + e, function (rdata) { - var myChartDisk = echarts.init(document.getElementById('diskview')); - var rData = []; - var wData = []; - var xData = []; - //var yData = []; - //var zData = []; + $.get('/ajax?action=GetDiskIo&start=' + b + '&end=' + e, function (rdata) { + var myChartDisk = echarts.init(document.getElementById('diskview')); + var rData = []; + var wData = []; + var xData = []; + var yData = []; + var zData = []; - for (var i = 0; i < rdata.length; i++) { - rData.push((rdata[i].read_bytes / 1024 / 60).toFixed(2)); - wData.push((rdata[i].write_bytes / 1024 / 60).toFixed(2)); - xData.push(rdata[i].addtime); - //yData.push(rdata[i].read_count); - //zData.push(rdata[i].write_count); - } - option = { - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'cross' - }, - //formatter: lan.control.time+":{b0}
{a0}: {c0} Kb/s
{a1}: {c1} Kb/s", - }, - legend: { - data: [lan.control.disk_read_bytes, lan.control.disk_write_bytes] - }, - xAxis: { - type: 'category', - boundaryGap: false, - data: xData, - axisLine: { - lineStyle: { - color: "#666" - } - } - }, - yAxis: { - type: 'value', - name: lan.index.unit + ':KB/s', - boundaryGap: [0, '100%'], - splitLine: { - lineStyle: { - color: "#ddd" - } - }, - axisLine: { - lineStyle: { - color: "#666" - } - } - }, - dataZoom: [{ - type: 'inside', - start: 0, - end: 100, - zoomLock: true - }, { - start: 0, - end: 100, - handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', - handleSize: '80%', - handleStyle: { - color: '#fff', - shadowBlur: 3, - shadowColor: 'rgba(0, 0, 0, 0.6)', - shadowOffsetX: 2, - shadowOffsetY: 2 - } - }], - series: [ - { - name: lan.control.disk_read_bytes, - type: 'line', - smooth: true, - symbol: 'none', - sampling: 'average', - itemStyle: { - normal: { - color: 'rgb(255, 70, 131)' - } - }, - data: rData - }, - { - name: lan.control.disk_write_bytes, - type: 'line', - smooth: true, - symbol: 'none', - sampling: 'average', - itemStyle: { - normal: { - color: 'rgba(46, 165, 186, .7)' - } - }, - data: wData - } - ] - }; - myChartDisk.setOption(option); - window.addEventListener("resize", function () { - myChartDisk.resize(); - }); - }) + for (var i = 0; i < rdata.length; i++) { + rData.push((rdata[i].read_bytes / 1024 / 60).toFixed(2)); + wData.push((rdata[i].write_bytes / 1024 / 60).toFixed(2)); + xData.push(rdata[i].addtime); + yData.push(rdata[i].read_count); + zData.push(rdata[i].read_time); + } + option = { + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross' + }, + formatter: function (config) { + var _tips = ''; + var unit = 'KB/s'; + var time = config[0].axisValue; + var _style = ' ' + item.seriesName + ': '; + if (item.seriesName == lan.control.disk_read_bytes || item.seriesName == lan.control.disk_write_bytes) { + _tips += item.data + unit + (config.length - 1 !== i ? '
' : ''); + } else { + if (item.seriesName == lan.control.disk_rw_count) { + _tips += item.data + '/s' + (config.length - 1 !== i ? '
' : ''); + }else{ + _tips += item.data + 'ms' + (config.length - 1 !== i ? '
' : ''); + } + } + } + return lan.control.time + ": " + time + "
" + _tips; + } + //formatter: lan.control.time+":{b0}
{a0}: {c0} Kb/s
{a1}: {c1} Kb/s", + }, + legend: { + data: [ + lan.control.disk_read_bytes, + lan.control.disk_write_bytes, + lan.control.disk_rw_count, + lan.control.disk_rw_time + ] + }, + xAxis: { + type: 'category', + boundaryGap: false, + data: xData, + axisLine: { + lineStyle: { + color: "#666" + } + } + }, + yAxis: { + type: 'value', + name: lan.index.unit + ':KB/s', + boundaryGap: [0, '20%'], + splitLine: { + lineStyle: { + color: "#ddd" + } + }, + axisLine: { + lineStyle: { + color: "#666" + } + } + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100, + zoomLock: true + }, + { + start: 0, + end: 100, + handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', + handleSize: '80%', + handleStyle: { + color: '#fff', + shadowBlur: 3, + shadowColor: 'rgba(0, 0, 0, 0.6)', + shadowOffsetX: 2, + shadowOffsetY: 2 + } + } + ], + series: [ + { + name: lan.control.disk_read_bytes, + type: 'line', + symbol: 'none', + itemStyle: { + normal: { + color: 'rgb(255, 70, 131)' + } + }, + data: rData + }, + { + name: lan.control.disk_write_bytes, + type: 'line', + symbol: 'none', + itemStyle: { + normal: { + color: 'rgba(46, 165, 186, .7)' + } + }, + data: wData + }, + { + name: lan.control.disk_rw_count, + type: 'line', + symbol: 'none', + itemStyle: { + normal: { + color: 'rgba(30, 144, 255)' + } + }, + data: yData + }, + { + name: lan.control.disk_rw_time, + type: 'line', + symbol: 'none', + itemStyle: { + normal: { + color: 'rgba(255, 140, 0)' + } + }, + data: zData + } + ] + }; + myChartDisk.clear() + myChartDisk.setOption(option, true); + window.addEventListener("resize", function () { + myChartDisk.resize(); + }); + }); } //网络Io @@ -513,7 +563,7 @@ $.get('/ajax?action=GetNetWorkIo&start='+b+'&end='+e,function(rdata){ yAxis: { type: 'value', name: lan.index.unit+':KB/s', - boundaryGap: [0, '100%'], + boundaryGap: [0, '20%'], splitLine:{ lineStyle:{ color:"#ddd" @@ -572,7 +622,8 @@ $.get('/ajax?action=GetNetWorkIo&start='+b+'&end='+e,function(rdata){ } ] }; - myChartNetwork.setOption(option); + myChartNetwork.clear() + myChartNetwork.setOption(option, true); window.addEventListener("resize",function(){ myChartNetwork.resize(); }); @@ -702,7 +753,8 @@ $.get('/ajax?action=get_load_average&start='+b+'&end='+e,function(rdata){ } ] }; - myChartgetload.setOption(option); + myChartgetload.clear() + myChartgetload.setOption(option, true); window.addEventListener("resize",function(){ myChartgetload.resize(); }); @@ -783,6 +835,8 @@ function getload(b,e){ ], yAxis: [{ scale: true, + min: 0, + max: 100, name: lan.control.resource_usage, splitLine: { // y轴网格显示 show: true, @@ -923,7 +977,8 @@ function getload(b,e){ fontSize: 12 } } - myChartgetload.setOption(option); + myChartgetload.clear() + myChartgetload.setOption(option, true); window.addEventListener("resize",function(){ myChartgetload.resize(); }) diff --git a/BTPanel/static/js/crontab.js b/BTPanel/static/js/crontab.js index 769d6370..648b72ed 100644 --- a/BTPanel/static/js/crontab.js +++ b/BTPanel/static/js/crontab.js @@ -91,7 +91,7 @@ function getCronData(){ } // 编辑计划任务 function edit_task_info(id){ - layer.msg(lan.public.the_get,{icon:16,time:0,shade: [0.3, '#000']}); + var load = layer.msg(lan.public.the_get,{icon:16,time:0,shade: [0.3, '#000']}); $.post('/crontab?action=get_crond_find',{id:id},function(rdata){ layer.closeAll(); var sTypeName = '',sTypeDom = '',cycleName = '',cycleDom = '',weekName = '',weekDom = '',sNameName ='',sNameDom = '',backupsName = '',backupsDom =''; @@ -121,7 +121,7 @@ function edit_task_info(id){ }, sTypeArray:[['toShell',lan.crontab.shell],['site',lan.crontab.site_bak],['database',lan.crontab.db_bak],['logs',lan.crontab.log_split],['path',lan.crontab.dir_bak],['rememory',lan.crontab.mem_release],['toUrl',lan.crontab.get_url]], cycleArray:[['day',lan.crontab.daily],['day-n',lan.crontab.n_day],['hour',lan.crontab.hourly],['hour-n',lan.crontab.n_hour],['minute-n',lan.crontab.n_min],['week',lan.crontab.weekly],['month',lan.crontab.monthly]], - weekArray:[[1,lan.crontab.TZZ1],[2,lan.crontab.TZZ2],[3,lan.crontab.TZZ3],[4,lan.crontab.TZZ4],[5,lan.crontab.TZZ5],[6,lan.crontab.TZZ6],[7,lan.crontab.TZZ7]], + weekArray:[[1,lan.crontab.TZZ1],[2,lan.crontab.TZZ2],[3,lan.crontab.TZZ3],[4,lan.crontab.TZZ4],[5,lan.crontab.TZZ5],[6,lan.crontab.TZZ6],[0,lan.crontab.TZZ7]], sNameArray:[], backupsArray:[], create:function(callback){ @@ -134,9 +134,18 @@ function edit_task_info(id){ cycleDom += '
  • '+ obj['cycleArray'][i][1] +'
  • '; } for(var i = 0; i '+ obj['weekArray'][i][1] +''; } + if(obj.from.notice_channel == 'telegram') { + obj.sBody.title = 'Telegram' + }else if(obj.from.notice_channel == 'mail') { + obj.sBody.title = 'Email' + }else if(obj.from.notice_channel == 'telegram,mail') { + obj.sBody.title = 'All' + } else { + obj.sBody.title = 'No Data' + } if(obj.from.sType == 'site' || obj.from.sType == 'database' || obj.from.sType == 'path' || obj.from.sType == 'logs'){ $.post('/crontab?action=GetDataList',{type:obj.from.sType == 'database'?'databases':'sites'},function(rdata){ obj.sNameArray = rdata.data; @@ -154,61 +163,57 @@ function edit_task_info(id){ if(obj.from.sType == 'webshell'){ edit_message_channel(obj.from.urladdress) } - }); - } - if(obj.from.notice_channel == 'telegram') { - obj.sBody.title = 'Telegram' - }else if(obj.from.notice_channel == 'mail') { - obj.sBody.title = 'Email' - }else if(obj.from.notice_channel == 'telegram,mail') { - obj.sBody.title = 'All' - } else { - obj.sBody.title = 'No Data' - } - if(obj.from.sType == 'site' || obj.from.sType == 'database' || obj.from.sType == 'path') { - $.post('/config?action=get_settings2',{type: 'sites'},function(rdata){ - if(rdata.user_mail.user_name && rdata.telegram.setup) { - obj.sBody.messageChannelBtnText = 'All' - obj.sBody.channelInitVal= 'user_name,telegram' - obj.sBody.messageChannelDom = '
  • All
  • Telegram
  • Email
  • ' - } else if(!rdata.user_mail.user_name && !rdata.telegram.setup){ - obj.sBody.messageChannelBtnText = 'No Data' - obj.sBody.channelInitVal= '' - obj.sBody.messageChannelDom += '
  • No Data
  • ' - } else if(rdata.telegram.setup) { - obj.sBody.messageChannelBtnText = 'Telegram' - obj.sBody.channelInitVal= 'telegram' - obj.sBody.messageChannelDom += '
  • Telegram
  • ' - } else if(rdata.user_mail.user_name) { - obj.sBody.messageChannelBtnText = 'Email' - obj.sBody.channelInitVal= 'mail' - obj.sBody.messageChannelDom += '
  • Email
  • ' + if(obj.from.sType == 'site' || obj.from.sType == 'database' || obj.from.sType == 'path') { + $.post('/config?action=get_settings2',{type: 'sites'},function(rdata){ + if(rdata.user_mail.user_name && rdata.telegram.setup) { + obj.sBody.messageChannelBtnText = 'All' + obj.sBody.channelInitVal= 'user_name,telegram' + obj.sBody.messageChannelDom = '
  • All
  • Telegram
  • Email
  • ' + } else if(!rdata.user_mail.user_name && !rdata.telegram.setup){ + obj.sBody.messageChannelBtnText = 'No Data' + obj.sBody.channelInitVal= '' + obj.sBody.messageChannelDom += '
  • No Data
  • ' + } else if(rdata.telegram.setup) { + obj.sBody.messageChannelBtnText = 'Telegram' + obj.sBody.channelInitVal= 'telegram' + obj.sBody.messageChannelDom += '
  • Telegram
  • ' + } else if(rdata.user_mail.user_name) { + obj.sBody.messageChannelBtnText = 'Email' + obj.sBody.channelInitVal= 'mail' + obj.sBody.messageChannelDom += '
  • Email
  • ' + } + layer.close(load); + callback(); + }) + } else { + layer.close(load); + callback(); } - // callback(); - }) - } - setTimeout(function() { + }); + } else { + layer.close(load); callback(); - },200) + } } }; obj.create(function(){ layer.open({ - type:1, - title:lan.crontab.crontab_edit+'-['+rdata.name+']', - area: '866px', - skin:'layer-create-content', - shadeClose:false, - closeBtn:2, - content:'
    \ + type: 1, + title: lan.crontab.crontab_edit + '-[' + rdata.name + ']', + area: '900px', + skin: 'layer-create-content', + shadeClose: false, + closeBtn: 2, + content: '\ +
    \
    \ - '+lan.crontab.task_type+'\ + ' + lan.crontab.task_type + '\ \
    \
    \ @@ -222,68 +227,94 @@ function edit_task_info(id){ '+ cycleName +'\ \ \ - \ + \
    \
    \ - \
    \
    \ - '+ sTypeName +'\ -
    \ + ' + sTypeName + '\
    \ -
    \ -
    \ -
    '+lan.crontab.bakup_to+'
    \ - \ -
    '+lan.crontab.keep_update+'
    \ +
    \ +
    \ + \ +
    \ +
    \ +
    ' + lan.crontab.bakup_to + '
    \ + \ +
    ' + lan.crontab.keep_update + '
    \
    \ - '+lan.crontab.copies+'\ + \ + \ + \ + ' + lan.crontab.copies + '\
    \ +
    \ + \ + \ + Keep local backup\
    \ +
    \
    \
    \ - Backup reminder\ -
    \ -
    notification
    \ - \ +
    notification
    \ + \ -
    \ - Keep local backup\ -
    \
    \
    \
    \ @@ -305,169 +336,170 @@ function edit_task_info(id){ Scan site\ \

    \

    Message channel
    \ \

    \ -
    \ -
    '+lan.crontab.edit_save+'
    \ -
    \ +
    \ +
    ' + lan.crontab.edit_save + '
    \ +
    \
    \ -
    ' - }); - getselectnoticename(); - setTimeout(function(){ - if(obj.from.sType == 'toShell'){ - $('.site_list').hide(); - }else if(obj.from.sType == 'rememory'){ - $('.site_list').hide(); - }else if( obj.from.sType == 'toUrl'){ - $('.site_list').hide(); - }else if( obj.from.sType == 'webshell'){ - $('.site_list').hide(); - }else{ - $('.site_list').show(); - } - - $('.sName_create').blur(function () { - obj.from.name = $(this).val(); - }); - $('.where1_create').blur(function () { - obj.from.where1 = $(this).val(); - }); - - $('.hour_create').blur(function () { - obj.from.hour = $(this).val(); - }); - - $('.minute_create').blur(function () { - obj.from.minute = $(this).val(); - }); - - $('.save_create').blur(function () { - obj.from.save = $(this).val(); - }); - - $('.sBody_create').blur(function () { - obj.from.sBody = $(this).val(); - }); - $('.url_create').blur(function () { - obj.from.urladdress = $(this).val(); - }); - - $('[aria-labelledby="cycle"] a').unbind().click(function () { - $('.cycle_btn').find('b').attr('val',$(this).attr('value')).html($(this).html()); - var type = $(this).attr('value'); - switch(type){ - case 'day': - $('.week_btn').hide(); - $('.where1_input').hide(); - $('.hour_input').show().find('input').val('1'); - $('.minute_input').show().find('input').val('30'); - obj.from.week = ''; - obj.from.type = ''; - obj.from.hour = 1; - obj.from.minute = 30; - break; - case 'day-n': - $('.week_btn').hide(); - $('.where1_input').show().find('input').val('1'); - $('.hour_input').show().find('input').val('1'); - $('.minute_input').show().find('input').val('30'); - obj.from.week = ''; - obj.from.where1 = 1; - obj.from.hour = 1; - obj.from.minute = 30; - break; - case 'hour': - $('.week_btn').hide(); - $('.where1_input').hide(); - $('.hour_input').hide(); - $('.minute_input').show().find('input').val('30'); - obj.from.week = ''; - obj.from.where1 = ''; - obj.from.hour = ''; - obj.from.minute = 30; - break; - case 'hour-n': - $('.week_btn').hide(); - $('.where1_input').hide(); - $('.hour_input').show().find('input').val('1'); - $('.minute_input').show().find('input').val('30'); - obj.from.week = ''; - obj.from.where1 = ''; - obj.from.hour = 1; - obj.from.minute = 30; - break; - case 'minute-n': - $('.week_btn').hide(); - $('.where1_input').hide(); - $('.hour_input').hide(); - $('.minute_input').show(); - obj.from.week = ''; - obj.from.where1 = ''; - obj.from.hour = ''; - obj.from.minute = 30; - break; - case 'week': - $('.week_btn').show(); - $('.where1_input').hide(); - $('.hour_input').show(); - $('.minute_input').show(); - obj.from.week = 1; - obj.from.where1 = ''; - obj.from.hour = 1; - obj.from.minute = 30; - break; - case 'month': - $('.week_btn').hide(); - $('.where1_input').show(); - $('.hour_input').show(); - $('.minute_input').show(); - obj.from.week = ''; - obj.from.where1 = 1; - obj.from.hour = 1; - obj.from.minute = 30; - break; +
    \ + ', + success: function () { + getselectnoticename(); + if(obj.from.sType == 'toShell'){ + $('.site_list').hide(); + }else if(obj.from.sType == 'rememory'){ + $('.site_list').hide(); + }else if( obj.from.sType == 'toUrl'){ + $('.site_list').hide(); + }else if( obj.from.sType == 'webshell'){ + $('.site_list').hide(); + }else{ + $('.site_list').show(); } - obj.from.type = $(this).attr('value'); - }); - - $('[aria-labelledby="week"] a').unbind().click(function () { - $('.week_btn').find('b').attr('val',$(this).attr('value')).html($(this).html()); - obj.from.week = $(this).attr('value'); - }); - - $('[aria-labelledby="backupTo"] a').unbind().click(function () { - $('.backup_btn').find('b').attr('val',$(this).attr('value')).html($(this).html()); - obj.from.backupTo = $(this).attr('value'); - }); - $('.plan-submits').unbind().click(function(){ - if(obj.from.type == 'hour-n'){ - obj.from.where1 = obj.from.hour; - obj.from.hour = ''; - }else if(obj.from.type == 'minute-n'){ - obj.from.where1 = obj.from.minute; - obj.from.minute = ''; - }else if(obj.from.sType == 'webshell'){ - obj.from.urladdress = $(".message_start input:checked").val() - } - obj.from.save_local = $('#modSave_local').val() - obj.from.notice = $("#modNotice").find("button b").attr("val") - obj.from.notice_channel = $("#modNotice_channelValue").attr("val") - layer.msg(lan.crontab.saving,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/crontab?action=modify_crond',obj.from,function(rdata){ - layer.closeAll(); - getCronData(); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); + + $('.sName_create').blur(function () { + obj.from.name = $(this).val(); }); - }); - },100); + $('.where1_create').blur(function () { + obj.from.where1 = $(this).val(); + }); + + $('.hour_create').blur(function () { + obj.from.hour = $(this).val(); + }); + + $('.minute_create').blur(function () { + obj.from.minute = $(this).val(); + }); + + $('.save_create').blur(function () { + obj.from.save = $(this).val(); + }); + + $('.sBody_create').blur(function () { + obj.from.sBody = $(this).val(); + }); + $('.url_create').blur(function () { + obj.from.urladdress = $(this).val(); + }); + + $('[aria-labelledby="cycle"] a').unbind().click(function () { + $('.cycle_btn').find('b').attr('val',$(this).attr('value')).html($(this).html()); + var type = $(this).attr('value'); + switch(type){ + case 'day': + $('.week_btn').hide(); + $('.where1_input').hide(); + $('.hour_input').show().find('input').val('1'); + $('.minute_input').show().find('input').val('30'); + obj.from.week = ''; + obj.from.type = ''; + obj.from.hour = 1; + obj.from.minute = 30; + break; + case 'day-n': + $('.week_btn').hide(); + $('.where1_input').show().find('input').val('1'); + $('.hour_input').show().find('input').val('1'); + $('.minute_input').show().find('input').val('30'); + obj.from.week = ''; + obj.from.where1 = 1; + obj.from.hour = 1; + obj.from.minute = 30; + break; + case 'hour': + $('.week_btn').hide(); + $('.where1_input').hide(); + $('.hour_input').hide(); + $('.minute_input').show().find('input').val('30'); + obj.from.week = ''; + obj.from.where1 = ''; + obj.from.hour = ''; + obj.from.minute = 30; + break; + case 'hour-n': + $('.week_btn').hide(); + $('.where1_input').hide(); + $('.hour_input').show().find('input').val('1'); + $('.minute_input').show().find('input').val('30'); + obj.from.week = ''; + obj.from.where1 = ''; + obj.from.hour = 1; + obj.from.minute = 30; + break; + case 'minute-n': + $('.week_btn').hide(); + $('.where1_input').hide(); + $('.hour_input').hide(); + $('.minute_input').show(); + obj.from.week = ''; + obj.from.where1 = ''; + obj.from.hour = ''; + obj.from.minute = 30; + break; + case 'week': + $('.week_btn').show(); + $('.where1_input').hide(); + $('.hour_input').show(); + $('.minute_input').show(); + obj.from.week = 1; + obj.from.where1 = ''; + obj.from.hour = 1; + obj.from.minute = 30; + break; + case 'month': + $('.week_btn').hide(); + $('.where1_input').show(); + $('.hour_input').show(); + $('.minute_input').show(); + obj.from.week = ''; + obj.from.where1 = 1; + obj.from.hour = 1; + obj.from.minute = 30; + break; + } + obj.from.type = $(this).attr('value'); + }); + + $('[aria-labelledby="week"] a').unbind().click(function () { + $('.week_btn').find('b').attr('val',$(this).attr('value')).html($(this).html()); + obj.from.week = $(this).attr('value'); + }); + + $('[aria-labelledby="backupTo"] a').unbind().click(function () { + $('.backup_btn').find('b').attr('val',$(this).attr('value')).html($(this).html()); + obj.from.backupTo = $(this).attr('value'); + }); + $('.plan-submits').unbind().click(function(){ + if(obj.from.type == 'hour-n'){ + obj.from.where1 = obj.from.hour; + obj.from.hour = ''; + }else if(obj.from.type == 'minute-n'){ + obj.from.where1 = obj.from.minute; + obj.from.minute = ''; + }else if(obj.from.sType == 'webshell'){ + obj.from.urladdress = $(".message_start input:checked").val() + } + obj.from.save_local = $('input[name="mod_save_local"]').is(':checked') ? 1 : 0; + obj.from.notice = $("#modNotice").find("button b").attr("val") + obj.from.notice_channel = $("#modNotice_channelValue").attr("val") + layer.msg(lan.crontab.saving,{icon:16,time:0,shade: [0.3, '#000']}); + $.post('/crontab?action=modify_crond',obj.from,function(rdata){ + layer.closeAll(); + getCronData(); + layer.msg(rdata.msg,{icon:rdata.status?1:2}); + }); + }); + } + }); }); }); @@ -658,7 +690,6 @@ function planAdd(){ case 'month': is1=31; break; - } if(where1 > is1 || where1 < is2){ @@ -756,7 +787,8 @@ function planAdd(){ data = data.replace('&sName=&','&sName='+ encodeURIComponent($('#filePath').val()) +'&') } if(data.indexOf('&save_local=') == -1) { - data = data + '&save_local='+ $('#save_local').val() +'¬ice='+ $("#notice").find("b").attr("val") +'¬ice_channel='+ $("#notice_channel").find("b").attr("val") +'' + var save_local = $('input[name="save_local"]').is(':checked') ? 1 : 0; + data = data + '&save_local=' + save_local + '¬ice=' + $("#notice").find("b").attr("val") + '¬ice_channel=' + $("#notice_channel").find("b").attr("val") + '' } $.post('/crontab?action=AddCrontab',data,function(rdata){ layer.closeAll(); @@ -894,171 +926,211 @@ $(".dropdown ul li a").click(function(){ //备份 -function toBackup(type){ - var sMsg = ""; - switch(type){ - case 'sites': - sMsg = lan.crontab.backup_site; - sType = "sites"; - break; - case 'databases': - sMsg = lan.crontab.backup_database; - sType = "databases"; - break; - case 'logs': - sMsg = lan.crontab.backup_log; - sType = "sites"; - break; - case 'path': - sMsg = lan.crontab.dir_bak; - sType = "sites"; - break; - } - var data='type='+sType - $.post('/crontab?action=GetDataList',data,function(rdata){ - $(".planname input[name='name']").attr('readonly','true').css({"background-color":"#f6f6f6","color":"#666"}); - if(type != 'path'){ - var sOpt = "",sOptBody = ''; - if(rdata.data.length == 0){ - layer.msg(lan.public.list_empty,{icon:2}) - return - } - for(var i=0;i'+rdata.data[i].name+'['+rdata.data[i].ps+']'; - } - sOptBody ='' - }else{ - $(".planname input[name='name']").val(sMsg+'[/www/wwwroot/]'); - sOptBody = '
    ' - setCookie('default_dir_path','/www/wwwroot/'); - setCookie('path_dir_change','/www/wwwroot/'); - setInterval(function(){ - if(getCookie('path_dir_change') != getCookie('default_dir_path')){ - var path_dir_change = getCookie('path_dir_change') - $(".planname input").val(lan.crontab.dir_bak+'['+getCookie('path_dir_change')+']'); - setCookie('default_dir_path',path_dir_change); - } - },500); - } - var orderOpt = '' - for (var i=0;i'+rdata.orderOpt[i].name+'' - } - var save_num = 3; - if(type === 'logs'){ - $('#cycle b').attr('val','day').text(lan.crontab.daily); - $('.planweek').hide(); - $('[name="hour"]').val(0); - $('[name="minute"]').val(1); - // $('#implement').parent().after('
    提示
    根据网络安全法第二十一条规定,网络日志应留存不少于六个月。
    ') - save_num = 180; - }else{ - $('#logs_tips').remove(); - } - var sBody = sOptBody + '
    '+lan.crontab.backup_to+'
    \ - \ -
    '+lan.crontab.save_new+'
    \ - \ -
    '; - if (type == 'sites' || type == 'path' || type == 'databases') { - $.post('/config?action=get_settings2',data,function(rdata){ - var messageChannelDom = '', messageChannelBtnText = '', channelInitVal = '' - if(rdata.user_mail.user_name && rdata.telegram.setup) { - messageChannelBtnText = 'ALL' - channelInitVal= 'user_name,telegram' - messageChannelDom = '
  • ALL
  • Telegram
  • Email
  • ' - } else if(!rdata.user_mail.user_name && !rdata.telegram.setup){ - messageChannelBtnText = 'No Data' - channelInitVal= '' - messageChannelDom += '
  • No Data
  • ' - } else if(rdata.telegram.setup) { - messageChannelBtnText = '钉钉' - channelInitVal= 'telegram' - messageChannelDom += '
  • Telegram
  • ' - } else if(rdata.user_mail.user_name) { - messageChannelBtnText = 'Email' - channelInitVal= 'mail' - messageChannelDom += '
  • Email
  • ' - } - sBody += '

    \ -

    Backup reminder
    \ - \ -
    \ - \ - \ -
    \ - Set notifications\ - \ -

    '; - if(type == 'sites' || type == "path") { - sBody += '

    \ -

    '+lan.crontab.exclusion_rule+'
    \ - \ -

    '; - } - $("#implement").html(sBody); - getselectnoticename(); - }) +function toBackup(type) { + var sMsg = ''; + switch (type) { + case 'sites': + sMsg = lan.crontab.backup_site; + sType = "sites"; + break; + case 'databases': + sMsg = lan.crontab.backup_database; + sType = "databases"; + break; + case 'logs': + sMsg = lan.crontab.backup_log; + sType = "sites"; + break; + case 'path': + sMsg = lan.crontab.dir_bak; + sType = "sites"; + break; + } + var data = 'type=' + sType; + $.post('/crontab?action=GetDataList', data, function (rdata) { + $(".planname input[name='name']").attr('readonly', 'true').css({"background-color": "#f6f6f6", "color": "#666"}); + if (type != 'path') { + var sOpt = ''; + var sOptBody = ''; + if (rdata.data.length == 0) { + layer.msg(lan.public.list_empty, {icon: 2}); + return + } + for (var i = 0; i < rdata.data.length; i++) { + if (type === 'logs') { + $(".planname input[name='name']").val(sMsg + '[ALL]'); } else { - $("#implement").html('
    '); - sBody += '

    \ -

    '+lan.crontab.exclusion_rule+'
    \ + if (i == 0) { + $(".planname input[name='name']").val(sMsg + '[' + rdata.data[i].name + ']'); + } + } + sOpt += '
  • ' + rdata.data[i].name + '[' + rdata.data[i].ps + ']
  • '; + } + sOptBody = '\ + \ + '; + } else { + $(".planname input[name='name']").val(sMsg + '[/www/wwwroot/]'); + sOptBody = '\ +
    \ + \ + \ +
    \ + '; + setCookie('default_dir_path', '/www/wwwroot/'); + setCookie('path_dir_change', '/www/wwwroot/'); + setInterval(function () { + if (getCookie('path_dir_change') != getCookie('default_dir_path')) { + var path_dir_change = getCookie('path_dir_change') + $(".planname input").val(lan.crontab.dir_bak + '[' + getCookie('path_dir_change') + ']'); + setCookie('default_dir_path', path_dir_change); + } + }, 500); + } + var orderOpt = '' + for (var i = 0; i < rdata.orderOpt.length; i++) { + orderOpt += '\ +
  • \ + ' + rdata.orderOpt[i].name + '\ +
  • \ + '; + } + var save_num = 3; + if (type === 'logs') { + $('#cycle b').attr('val', 'day').text(lan.crontab.daily); + $('.planweek').hide(); + $('[name="hour"]').val(0); + $('[name="minute"]').val(1); + // $('#implement').parent().after('
    提示
    根据网络安全法第二十一条规定,网络日志应留存不少于六个月。
    ') + save_num = 180; + } else { + $('#logs_tips').remove(); + } + var sBody = sOptBody + '\ +
    ' + lan.crontab.backup_to + '
    \ + \ +
    ' + lan.crontab.save_new + '
    \ +
    \ + \ + \ + \ +
    \ + \ + '; + if (type == 'sites' || type == 'path' || type == 'databases') { + $.post('/config?action=get_settings2', data, function (rdata) { + var messageChannelDom = ''; + var messageChannelBtnText = ''; + var channelInitVal = ''; + if (rdata.user_mail.user_name && rdata.telegram.setup) { + messageChannelBtnText = 'ALL'; + channelInitVal = 'user_name,telegram'; + messageChannelDom = '\ +
  • ALL
  • \ +
  • Telegram
  • \ +
  • Email
  • \ + '; + } else if (!rdata.user_mail.user_name && !rdata.telegram.setup) { + messageChannelBtnText = 'No Data' + channelInitVal = '' + messageChannelDom += '
  • No Data
  • '; + } else if (rdata.telegram.setup) { + messageChannelBtnText = 'Telegram' + channelInitVal = 'telegram' + messageChannelDom += '
  • Telegram
  • '; + } else if (rdata.user_mail.user_name) { + messageChannelBtnText = 'Email' + channelInitVal = 'mail' + messageChannelDom += '
  • Email
  • '; + } + sBody += '\ +

    \ +

    Backup reminder
    \ + \ + \ + \ + Set notifications\ +

    \ + '; + if (type == 'sites' || type == "path") { + sBody += '\ +

    \ +

    ' + lan.crontab.exclusion_rule + '
    \ \ -

    '; - $("#implement").html(sBody); - getselectname(); - } - $("#implement").on('click','.dropdown ul li a',function(ev){ - var sName = $("#sName").attr("val"); - if(!sName) return; - $(".planname input[name='name']").val(sMsg+'['+sName+']'); - }); - if(type == "path"){ - $('.planname input').attr('readonly',false).removeAttr('style'); - } - }); +

    \ + '; + } + $("#implement").html(sBody); + getselectnoticename(); + }); + } else { + $("#implement").html('
    '); + sBody += '\ +

    \ +

    ' + lan.crontab.exclusion_rule + '
    \ + \ +

    \ + '; + $("#implement").html(sBody); + getselectname(); + } + $("#implement").on('click', '.dropdown ul li a', function (ev) { + var sName = $("#sName").attr("val"); + if (!sName) return; + $(".planname input[name='name']").val(sMsg + '[' + sName + ']'); + }); + if (type == "path") { + $('.planname input').attr('readonly', false).removeAttr('style'); + } + }); } //下拉菜单名称 @@ -1282,7 +1354,7 @@ function modSelSave_local() { } } -//下拉菜单名称 +// 下拉菜单名称 function getselectnoticename(){ $(".dropdown ul li a").click(function(){ var txt = $(this).text(); @@ -1294,12 +1366,37 @@ function getselectnoticename(){ } else { $('#modNotice').nextAll().show() } - if($("#notice").find("button b").attr("val") == '0') { + if ($("#notice").find("button b").attr("val") == '0') { $('#messageChannelBox').hide() $('#notice_channel').hide() - }else { + } else { $('#messageChannelBox').show() $('#notice_channel').show() } }); } + +// 备份到 +$('body').on('click', '.serverDiskToLocal .dropdown-menu a', function () { + var value = $(this).attr('value'); + var $line = $(this).parents('.clearfix.plan').find('.local-backup-line'); + if (value == 'localhost') { + $line.find('input').prop('checked', false); + $line.find('.form-checkbox').removeClass('active'); + $line.hide(); + } else { + $line.find('input').prop('checked', true); + $line.find('.form-checkbox').addClass('active'); + $line.show(); + } +}); + +// 单选框 +$('body').on('click', '.form-checkbox', function () { + $(this).toggleClass('active'); + $(this).next().prop('checked', $(this).hasClass('active')); +}); + +$('body').on('click', '.form-checkbox-label', function () { + $(this).siblings('.form-checkbox').click(); +}); diff --git a/BTPanel/static/js/database.js b/BTPanel/static/js/database.js index 01270940..2a9635e8 100644 --- a/BTPanel/static/js/database.js +++ b/BTPanel/static/js/database.js @@ -1,5 +1,7 @@ var database_table = {} var database = { + dbCloudServerTable: null, //远程服务器视图 + cloudDatabaseList: [], //远程服务器列表 init: function () { this.database_table_view(); var _this = this; @@ -10,247 +12,427 @@ var database = { } }); }, - database_table_view:function(search){ - $('#bt_database_table').empty(); - database_table = bt_tools.table({ - el: '#bt_database_table', - url: '/data?action=getData', - param: { - table: 'databases', - search:search|| '' - }, //参数 - minWidth: '1000px', - default: "Database list is empty", // 数据为空时的默认提示 - column:[ - { fid: 'id', type: 'checkbox', width: 30 }, - { - fid: 'name', - width: 120, - title: lan.database.add_name, - template: function (item) { - return '' + item.name + ''; + database_table_view: function (search) { + var that = this; + this.get_cloud_server_list(function () { + $('#bt_database_table').empty(); + var param = { table: 'databases', search: search || '' }; + database_table = bt_tools.table({ + el: '#bt_database_table', + url: '/data?action=getData', + param: param, //参数 + minWidth: '1000px', + autoHeight: true, + default: "Database list is empty", // 数据为空时的默认提示 + beforeRequest: function () { + var db_type_val = $('.database_type_select_filter').val(); + switch (db_type_val) { + case 'all': + delete param['db_type']; + delete param['sid']; + break; + case 0: + param['db_type'] = 0; + break; + default: + delete param['db_type']; + param['sid'] = db_type_val; } + return param; }, - { - fid: 'username', - width: 120, - title: lan.database.user, - sort: function () { - database_table.$refresh_table_list(true); - }, - template: function (item) { - return '' + item.username + ''; - } - }, - { - fid:'password', - width: 200, - title:lan.database.add_pass, - type:'password', - copy:true, - eye_open:true - }, - { - fid:'backup', - title: lan.database.backup, - width: 130, - template: function (item) { - var backup = lan.database.backup_empty, - _class = "bt_warning"; - if (item.backup_count > 0) backup = lan.database.backup_ok, _class = "bt_success"; - return '' + backup + (item.backup_count > 0 ? ('(' + item.backup_count + ')') : '') + ' | ' + - ''+lan.database.input+''; - } - }, - // { - // fid: 'ps', title: lan.database.add_ps, templet: function (item) { - // var _ps = "" - // if (item.password) { - // _ps += item.ps - // } else { - // _ps += lan.database.cant_get_pass+''+lan.database.edit_pass+''+lan.database.button_set_pass+'!'; - // } - // _ps += ""; - // return _ps; - // } - // }, - { - fid: 'ps', - title: lan.database.add_ps, - type: 'input', - blur: function (row, index, ev) { - bt.pub.set_data_ps({ - id: row.id, - table: 'databases', - ps: ev.target.value - }, function (res) { - layer.msg(res.msg, (res.status ? {} : { - icon: 2 - })); - }); - }, - keyup: function (row, index, ev) { - if (ev.keyCode === 13) { - $(this).blur(); - } - } - }, - { - type: 'group', - title: lan.database.operation, - width: 280, - align: 'right', - group: [{ - title: lan.database.admin, - tips: lan.database.admin_title, - event: function(row) { - bt.database.open_phpmyadmin(row.name,row.username,row.password); - } - },{ - title: lan.database.auth, - tips:lan.database.set_db_auth, - event: function(row) { - bt.database.set_data_access(row.username); - } - },{ - title:lan.database.tools, - tips:lan.database.mysql_tools, - event: function(row){ - database.rep_tools(row.name); - } - },{ - title:lan.database.edit_pass, - tips:lan.database.edit_pass_title, - event: function(row){ - database.set_data_pass(row.id,row.username,row.password); - } - },{ - title:lan.database.del, - tips:lan.database.del_title, - event: function(row){ - database.del_database(row.id,row.name); - } - }] - } - ], - sortParam: function (data) { - return { - 'order': data.name + ' ' + data.sort - }; - }, - tootls: [{ // 按钮组 - type: 'group', - positon: ['left', 'top'], - list: [{ - title: lan.database.add_title, - active: true, - event: function () { - bt.database.add_database(function (res){ - if(res.status) database_table.$refresh_table_list(true); - }) - } - },{ - title: lan.database.edit_root, - event: function () { - bt.database.set_root('root') - } - },{ - title: 'phpMyAdmin', - event: function () { - bt.database.open_phpmyadmin('','root', bt.config.mysql_root) - } - },{ - title: 'Sync all', - style: {'margin-left':'30px'}, - event: function () { - database.sync_to_database(0) - } - },{ - title: 'Get DB from server', - event: function () { - // database.sync_to_database(1) - bt.database.sync_database(function (rdata) { - if (rdata.status) that.database_table.$refresh_table_list(true); - }); - } - }] - },{ - type: 'batch', //batch_btn - positon: ['left', 'bottom'], - placeholder: 'Select batch operation', - buttonValue: 'Execute', - disabledSelectValue: 'Select the DB to execute!!', - selectList: [{ - title:'Sync to Server', - url:'/database?action=SyncToDatabases&type=1', - paramName: 'ids', //列表参数名,可以为空 - paramId: 'id', // 需要传入批量的id - th:'Database Name', - beforeRequest: function(list) { - var arry = []; - $.each(list, function (index, item) { - arry.push(item.id); - }); - return JSON.stringify(arry) - }, - success: function (res, list, that) { - layer.closeAll(); - var html = ''; - $.each(list, function (index, item) { - html += '' + item.name + '
    ' + res.msg + '
    '; - }); - that.$batch_success_table({ - title: 'Batch sync selected', - th: 'Database Name', - html: html - }); - } - },{ - title: "Delete database", - url: '/database?action=DeleteDatabase', - load: true, - param: function (row) { - return { - id: row.id, - name: row.name + column:[ + { fid: 'id', type: 'checkbox', width: 20 }, + { + fid: 'name', + width: 120, + title: lan.database.add_name, + template: function (item) { + return '' + item.name + ''; } }, - callback: function (that) { // 手动执行,data参数包含所有选中的站点 - var ids = []; - for (var i = 0; i < that.check_list.length; i++) { - ids.push(that.check_list[i].id); - } - database.del_database(ids,function(param){ - that.start_batch(param, function (list) { - layer.closeAll() - var html = ''; - for (var i = 0; i < list.length; i++) { - var item = list[i]; - html += '' + item.name + '
    ' + item.request.msg + '
    '; - } - database_table.$batch_success_table({ - title: 'Batch deletion', - th: 'Database Name', - html: html - }); - }); + { + fid: 'username', + width: 120, + title: lan.database.user, + sort: function () { database_table.$refresh_table_list(true); - }) + }, + template: function (item) { + return '' + item.username + ''; + } + }, + { + fid:'password', + width: 220, + title: lan.database.add_pass, + type: 'password', + copy: true, + eye_open: true, + template: function (row) { + var id = row.id; + var username = row.username; + var password = row.password; + if (row.password === '') return '' + lan.database.not_found_pwd_1 + '' + lan.database.not_found_pwd_2 + '' + lan.database.not_found_pwd_3 + '!'; + return true; + } + }, + bt.public.get_quota_config('database'), + { + fid: 'backup', + title: lan.database.backup, + width: 130, + template: function (item) { + var backup = lan.database.backup_empty, + _class = "bt_warning"; + if (item.backup_count > 0) backup = lan.database.backup_ok, _class = "bt_success"; + return '' + backup + (item.backup_count > 0 ? ('(' + item.backup_count + ')') : '') + ' | ' + + ''+lan.database.input+''; + } + }, + { + fid: 'position', + title: lan.database.position, + type: 'text', + template: function (row) { + var type_column = '-'; + var host = row.conn_config.db_host; + var port = row.conn_config.db_port; + switch(row.db_type){ + case 0: + type_column = lan.database.add_auth_local; + break; + case 1: + type_column = (lan.database.cloud_database + '(' + host + ':' + port + ')').toString(); + break; + case 2: + var list = that.cloudDatabaseList; + $.each(list, function(index, item) { + var db_host = item.db_host; + var db_port = item.db_port; + if (row.sid == item.id) { + // 默认显示备注 + if (item.ps !== '') { + type_column = item.ps + } else { + type_column = (lan.database.cloud_database + '(' + db_host + ':' + db_port + ')').toString(); + } + } + }); + break; + } + return '' + type_column + ''; + } + }, + { + fid: 'ps', + title: lan.database.add_ps, + type: 'input', + blur: function (row, index, ev) { + bt.pub.set_data_ps({ + id: row.id, + table: 'databases', + ps: ev.target.value + }, function (res) { + layer.msg(res.msg, (res.status ? {} : { + icon: 2 + })); + }); + }, + keyup: function (row, index, ev) { + if (ev.keyCode === 13) { + $(this).blur(); + } + } + }, + { + type: 'group', + title: lan.database.operation, + width: 280, + align: 'right', + group: [ + { + title: lan.database.admin, + tips: lan.database.admin_title, + hide: function (row) { + return row.db_type != 0 + }, + event: function(row) { + bt.database.open_phpmyadmin(row.name,row.username,row.password); + } + }, + { + title: lan.database.auth, + tips:lan.database.set_db_auth, + hide: function (row) { + return row.db_type == 1 + }, + event: function(row) { + bt.database.set_data_access(row.username); + } + }, + { + title:lan.database.tools, + tips:lan.database.mysql_tools, + event: function(row){ + database.rep_tools(row.name); + } + }, + { + title: lan.database.edit_pass, + tips: lan.database.edit_pass_title, + hide: function (row) { + return row.db_type == 1 + }, + event: function(row){ + database.set_data_pass(row.id,row.username,row.password); + } + }, + { + title: lan.database.del, + tips: lan.database.del_title, + event: function(row){ + database.del_database(row.id, row.name, row); + } + } + ] } - }] - }, { //分页显示 - type: 'page', - positon: ['right', 'bottom'], // 默认在右下角 - pageParam: 'p', //分页请求字段,默认为 : p - page: 1, //当前分页 默认:1 - numberParam: 'limit', //分页数量请求字段默认为 : limit - number: 20, //分页数量默认 : 20条 - numberList: [10, 20, 50, 100, 200], // 分页显示数量列表 - numberStatus: true, // 是否支持分页数量选择,默认禁用 - jump: true, //是否支持跳转分页,默认禁用 - }] + ], + sortParam: function (data) { + return { + 'order': data.name + ' ' + data.sort + }; + }, + tootls: [ + { // 按钮组 + type: 'group', + positon: ['left', 'top'], + list: [ + { + title: lan.database.add_title, + active: true, + event: function () { + that.generate_cloud_server_list(function (list) { + bt.database.add_database(list, function (res){ + if (res.status) database_table.$refresh_table_list(true); + }); + }); + } + }, + { + title: lan.database.edit_root, + event: function () { + bt.database.set_root('root') + } + }, + { + title: 'phpMyAdmin', + event: function () { + bt.database.open_phpmyadmin('','root', bt.config.mysql_root) + } + }, + { + title: lan.database.cloud_server, + event: function() { + database.open_cloud_server(); + } + },{ + title: 'Sync all', + style: { 'margin-left': '30px' }, + event: function () { + database.sync_to_database(0) + } + }, { + title: 'Get DB from server', + event: function () { + that.generate_cloud_server_list(function (list) { + bt_tools.open({ + title: lan.database.select_position, + area: '450px', + skin: 'databaseCloudServer', + btn: [lan.public.confirm, lan.public.cancel], + content: { + 'class':'pd20', + form:[ + { + label: lan.database.position, + group:{ + type: 'select', + name: 'sid', + width: '260px', + list: list + } + } + ] + }, + success: function ($layer) { + $layer.find('.layui-layer-content').css('overflow','inherit'); + }, + yes: function (form, index) { + bt.database.sync_database(form.sid, function (rdata) { + if (rdata.status) { + database_table.$refresh_table_list(true); + layer.close(index); + } + }); + } + }); + }); + } + } + // { + // title: 'Recycle bin', + // style: { + // 'position': 'absolute', + // 'right': '-5px' + // }, + // icon: 'trash', + // event: function () { + // bt.recycle_bin.open_recycle_bin(6) + // } + // } + ] + }, + { + type: 'batch', // batch_btn + positon: ['left', 'bottom'], + placeholder: 'Select batch operation', + buttonValue: 'Execute', + disabledSelectValue: 'Select the DB to execute!!', + selectList: [ + { + title: 'Sync to Server', + url: '/database?action=SyncToDatabases&type=1', + paramName: 'ids', //列表参数名,可以为空 + paramId: 'id', // 需要传入批量的id + th: 'Database Name', + refresh: true, + beforeRequest: function (list) { + var arry = []; + $.each(list, function (index, item) { + arry.push(item.id); + }); + return JSON.stringify(arry) + }, + success: function (res, list, that) { + layer.closeAll(); + var html = ''; + $.each(list, function (index, item) { + html += '' + item.name + '
    ' + res.msg + '
    '; + }); + that.$batch_success_table({ + title: 'Batch sync selected', + th: 'Database Name', + html: html + }); + } + }, { + title: "Delete database", + url: '/database?action=DeleteDatabase', + load: true, + refresh: true, + param: function (row) { + return { + id: row.id, + name: row.name + } + }, + callback: function (that) { + // 手动执行, data参数包含所有选中的站点 + var ids = []; + for (var i = 0; i < that.check_list.length; i++) { + ids.push(that.check_list[i].id); + } + database.del_database(ids, function(param){ + that.start_batch(param, function (list) { + layer.closeAll() + var html = ''; + for (var i = 0; i < list.length; i++) { + var item = list[i]; + html += '' + item.name + '
    ' + item.request.msg + '
    '; + } + database_table.$batch_success_table({ + title: 'Batch deletion', + th: 'Database Name', + html: html + }); + database_table.$refresh_table_list(true); + }); + }) + } + } + ] + }, + { + type: 'search', + positon: ['right', 'top'], + placeholder: lan.database.database_search, + searchParam: 'search', //搜索请求字段,默认为 search + value: '',// 当前内容,默认为空 + }, + { //分页显示 + type: 'page', + positon: ['right', 'bottom'], // 默认在右下角 + pageParam: 'p', //分页请求字段,默认为 : p + page: 1, //当前分页 默认:1 + numberParam: 'limit', //分页数量请求字段默认为 : limit + number: 20, //分页数量默认 : 20条 + numberList: [10, 20, 50, 100, 200], // 分页显示数量列表 + numberStatus: true, // 是否支持分页数量选择,默认禁用 + jump: true, //是否支持跳转分页,默认禁用 + } + ] + }); + // 未安装数据库 + if (!isSetup) { + $("button[title='phpMyAdmin']").hide(); + $("button[title='Root password']").hide(); + } + that.render_cloud_server_list(); }); }, + // 渲染远程数据库选择框 + render_cloud_server_list: function () { + if ($('.database_type_select_filter').length == 0) { + $('#bt_database_table .bt_search').before(''); + $('.database_type_select_filter').change(function () { + database_table.$refresh_table_list(true); + }); + } + var option = ''; + $.each(this.cloudDatabaseList, function (index, item) { + var tips = item.ps != '' ? item.ps : item.db_host; + option += ''; + }); + $('.database_type_select_filter').html(option); + }, + // 获取远程服务器列表 + get_cloud_server_list: function (callback) { + var that = this; + var loadT = bt.load(lan.database.get_cloud_list_tips); + bt.send('GetCloudServer', 'database/GetCloudServer', {}, function (cloudData) { + loadT.close(); + that.cloudDatabaseList = cloudData; + callback && callback(); + }); + }, + // 生成远程服务器列表 + generate_cloud_server_list: function (callback) { + var list = this.cloudDatabaseList; + if (list.length == 0) { + return layer.msg(lan.database.add_server_tips, { + time: 0, icon: 2, closeBtn: 2, shade: .3 + }); + } + var cloudList = []; + $.each(list, function (index, item) { + var ps = item.ps; + var host = item.db_host; + if (!ps || !host) return; + var tips = ps != '' ? (ps + ' (' + host + ')') : host; + cloudList.push({ title: tips, value: item.id }); + }); + callback && callback(cloudList); + }, rep_tools: function (db_name, res) { var loadT = layer.msg(lan.database.get_data, { icon: 16, time: 0 }); bt.send('GetInfo', 'database/GetInfo', { db_name: db_name }, function (rdata) { @@ -263,6 +445,7 @@ var database = { var tbody = ''; for (var i = 0; i < rdata.tables.length; i++) { if (!types[rdata.tables[i].type]) continue; + var setType = rdata.tables[i].type == 'InnoDB' ? types.MyISAM : types.InnoDB tbody += '\ \ ' + rdata.tables[i].table_name + '\ @@ -273,7 +456,7 @@ var database = { \ '+lan.database.backup_re+' |\ '+lan.database.optimization+' |\ - '+ lan.database.change + types[rdata.tables[i].type] + '\ + '+ lan.database.change + setType + '\ \ ' } @@ -288,7 +471,7 @@ var database = { layer.open({ type: 1, - title: lan.database.mysql_tools_box+"【" + db_name + "】", + title: lan.database.mysql_tools_box+" [ " + db_name + " ]", area: ['850px', '580px'], closeBtn: 2, shadeClose: false, @@ -405,11 +588,6 @@ var database = { if (rdata.status) database_table.$refresh_table_list(true); }); }, - sync_database: function () { - bt.database.sync_database(function (rdata) { - if (rdata.status) database_table.$refresh_table_list(true); - }) - }, add_database: function () { bt.database.add_database(function (rdata) { if (rdata.status) database_table.$refresh_table_list(true); @@ -449,9 +627,16 @@ var database = { break; } }, - del_database: function (wid, dbname, callback) { - var rendom = bt.get_random_code(),num1 = rendom['num1'],num2 = rendom['num2'],title = ''; + del_database: function (wid, dbname, obj, callback) { + var rendom = bt.get_random_code(); + var num1 = rendom['num1']; + var num2 = rendom['num2']; + var title = ''; + var tips = 'The deletion may affect the business!'; title = typeof dbname === "function" ?'Batch delete databases':'Delete database [ '+ dbname +' ]'; + if (obj && obj.db_type > 0) { + tips = lan.database.del_cloud_database_tips; + } layer.open({ type:1, title:title, @@ -462,7 +647,7 @@ var database = { shadeClose: true, content:"
    " + "" + - "
    The deletion may affect the business!
    " + + "
    " + tips + "
    " + "
    Note: The data is priceless, please operate with caution! ! !"+(!recycle_bin_db_open?'

    Risk: The DB recycle bin is not enabled, deleting will disappear forever!':'')+"
    " + "
    " + lan.bt.cal_msg + ""+ num1 +" + "+ num2 +"=
    " + "
    ", @@ -592,7 +777,7 @@ var database = { bt.open({ type: 1, skin: 'demo-class', - area: '700px', + area: ['700px', '400px'], title: lan.database.backup_title, closeBtn: 2, shift: 5, @@ -643,6 +828,242 @@ var database = { database.input_database(name); }); }, + // 打开远程服务器列表弹框 + open_cloud_server: function () { + var that = this; + bt_tools.open({ + title: lan.database.cloud_server_list, + area: ['860px', '400px'], + btn: false, + skin: 'databaseCloudServer', + content: '
    ', + success: function () { + that.dbCloudServerTable = bt_tools.table({ + el: '#db_cloud_server_table', + url: '/database?action=GetCloudServer', + default: lan.database.cloud_server_empty, + height: 300, + column: [ + { + fid: 'db_host', + title: lan.database.server_address, + width: 150, + template: function (item) { + return '' + item.db_host + ''; + } + }, + { + fid: 'db_port', + width: 100, + title: lan.database.port + }, + { + fid: 'db_user', + width: 120, + title: lan.database.user + }, + { + fid: 'db_password', + width: 190, + type: 'password', + title: lan.database.add_pass, + copy: true, + eye_open: true + }, + { + fid: 'ps', + title: lan.database.add_ps, + template: function (item) { + var ps = item.ps; + return '' + ps + ''; + } + }, + { + type: 'group', + width: 130, + title: lan.database.operation, + align: 'right', + group: [ + { + title: 'Get DB', + event: function (row) { + bt.database.sync_database(row.id, function (rdata) { + if (rdata.status) { + database_table.$refresh_table_list(true); + } + }); + } + }, + { + title: lan.public.edit, + event: function (row) { + that.render_db_cloud_server_view(row, true); + } + }, + { + title: lan.public.del, + event: function (row) { + that.del_db_cloud_server(row); + } + } + ] + } + ], + tootls:[ + { + type: 'group', + positon: ['left', 'top'], + list:[{ + title: lan.public.add + ' ' + lan.database.cloud_server, + active: true, + event: function() { + that.render_db_cloud_server_view(); + } + }] + } + ], + success: function (config) { + that.cloudDatabaseList = config.data; + } + }); + } + }); + }, + // 添加/编辑远程服务器视图 + render_db_cloud_server_view: function(config, is_edit) { + var that = this; + if (!config) { + config = { + db_host: '', + db_port: '3306', + db_user: '', + db_password: '', + db_user: 'root', + ps: '' + }; + } + var title = is_edit ? lan.public.edit : lan.public.add; + bt_tools.open({ + title: title + ' ' + lan.database.cloud_server, + area: '450px', + btn: [lan.public.save, lan.public.cancel], + skin: 'addCloudServerProject', + content:{ + 'class':'pd20', + form:[ + { + label: lan.database.server_address, + group:{ + type: 'text', + name: 'db_host', + width: '260px', + value: config.db_host, + placeholder: lan.database.input_server_address, + event: function () { + $('[name=db_host]').on('input', function () { + $('[name=db_ps]').val($(this).val()); + }); + } + } + }, + { + label: lan.database.port, + group: { + type: 'number', + name: 'db_port', + width: '260px', + value: config.db_port, + placeholder: lan.database.input_port + } + }, + { + label: lan.database.user, + group: { + type: 'text', + name: 'db_user', + width: '260px', + value: config.db_user, + placeholder: lan.database.input_username + } + }, + { + label: lan.database.add_pass, + group:{ + type: 'text', + name: 'db_password', + width: '260px', + value: config.db_password, + placeholder: lan.database.input_password + } + }, + { + label: lan.database.add_ps, + group:{ + type: 'text', + name: 'db_ps', + width: '260px', + value: config.ps, + placeholder: lan.database.server_note + } + }, + { + group: { + type: 'help', + style: {'margin-top':'0'}, + list: [ + lan.database.remote_help_1, + lan.database.remote_help_2, + lan.database.remote_help_3, + lan.database.remote_help_4 + ] + } + } + ] + }, + yes: function (form, indexs) { + var interface = is_edit ? 'ModifyCloudServer' : 'AddCloudServer'; + if (form.db_host == '') return layer.msg(lan.database.input_server_address, { icon: 2 }); + if (form.db_port == '') return layer.msg(lan.database.input_port, { icon: 2 }); + if (form.db_user == '') return layer.msg(lan.database.input_username, { icon: 2 }); + if (form.db_password == '') return layer.msg(lan.database.input_password, { icon: 2 }); + + if (is_edit) form['id'] = config['id']; + + var tips = is_edit ? lan.database.edit_cloud_server_tips : lan.database.add_cloud_server_tips; + var layerT = bt.load(tips); + bt.send(interface, 'database/' + interface, form, function (rdata) { + layerT.close(); + if (rdata.status) { + that.dbCloudServerTable.$refresh_table_list(); + layer.close(indexs); + layer.msg(rdata.msg, { icon: 1 }); + } else { + layer.msg(rdata.msg, { + time:0,icon:2,closeBtn: 2, shade: .3, area: '650px' + }); + } + }); + } + }); + }, + // 删除远程服务器管理关系 + del_db_cloud_server: function (row) { + var that = this; + bt.confirm({ + title: lan.public.del + ' [' + row.db_host + '] ' + lan.database.cloud_server, + msg: lan.database.del_cloud_server_tips + '!' + }, function () { + bt.send('RemoveCloudServer', 'database/RemoveCloudServer', { + id: row.id + }, function (rdata) { + if (rdata.status) { + database_table.$refresh_table_list(true); + that.dbCloudServerTable.$refresh_table_list(true); + } + bt.msg(rdata); + }); + }) + }, input_database: function (name) { var path = bt.get_cookie('backup_path') + "/database"; bt.send('get_files', 'files/GetDir', 'reverse=True&sort=mtime&tojs=GetFiles&p=1&showRow=100&path=' + path, function (rdata) { @@ -658,7 +1079,7 @@ var database = { bt.open({ type: 1, skin: 'demo-class', - area: '600px', + area: ["600px", "530px"], title: lan.database.input_title_file+'['+name+']', closeBtn: 2, shift: 5, diff --git a/BTPanel/static/js/files.js b/BTPanel/static/js/files.js index 689e19b0..1affdf68 100644 --- a/BTPanel/static/js/files.js +++ b/BTPanel/static/js/files.js @@ -68,516 +68,6 @@ var bt_file = { fix_permissions: lan.files.fix_permission, del_path_premissions: lan.files.deleting_permission, }, - file_drop:{ - f_path:null, - startTime: 0, - endTime:0, - uploadLength:0, //上传数量 - splitSize: 1024 * 1024 * 2, //文件上传分片大小 - splitEndTime: 0, - splitStartTime:0, - fileSize:0, - speedLastTime:0, - filesList:[], // 文件列表数组 - errorLength:0, //上传失败文件数量 - isUpload:true, //上传状态,是否可以上传 - uploadSuspend:[], //上传暂停参数 - isUploadNumber:800,//限制单次上传数量 - uploadAllSize:0, // 上传文件总大小 - uploadedSize:0, // 已上传文件大小 - updateedSizeLast:0, - topUploadedSize:0, // 上一次文件上传大小 - uploadExpectTime:0, // 预计上传时间 - initTimer:0, // 初始化计时 - speedInterval:null, //平局速度定时器 - timerSpeed:0, //速度 - isLayuiDrop:false, //是否是小窗口拖拽 - uploading:false, - is_webkit:(function(){ - if(navigator.userAgent.indexOf('WebKit') > -1) return true; - return false; - })(), - init:function(){ - if($('#mask_layer').length == 0) { - window.UploadFiles = function(){ bt_file.file_drop.dialog_view()}; - $("body").append($('')); - this.event_relation(document.querySelector('#container'),document,document.querySelector('#mask_layer')); - } - }, - // 事件关联 (进入,离开,放下) - event_relation:function(enter,leave,drop){ - var that = this,obj = Object.keys(arguments); - for(var item in arguments){ - if(typeof arguments[item] == "object" && typeof arguments[item].nodeType != 'undefined'){ - arguments[item] = { - el:arguments[item], - callback:null - } - } - } - leave.el.addEventListener("dragleave",(leave.callback != null)?leave.callback:function(e){ - if(e.x == 0 && e.y == 0) $('#mask_layer').hide(); - e.preventDefault(); - },false); - enter.el.addEventListener("dragenter", (enter.callback != null)?enter.callback:function(e){ - if(e.dataTransfer.items[0].kind == 'string') return false - $('#mask_layer').show(); - that.isLayuiDrop = false; - e.preventDefault(); - },false); - drop.el.addEventListener("dragover",function(e){ e.preventDefault() }, false); - drop.el.addEventListener("drop",(enter.callback != null)?drop.callback:that.ev_drop, false); - }, - - - // 事件触发 - ev_drop:function(e){ - if(e.dataTransfer.items[0].kind == 'string') return false; - if(!bt_file.file_drop.is_webkit){ - $('#mask_layer').hide(); - return false; - } - e.preventDefault(); - if(bt_file.file_drop.uploading){ - layer.msg('Uploading files, please wait...'); - return false; - } - var items = e.dataTransfer.items,time,num = 0; - loadT = layer.msg('Getting upload files details, please wait...',{icon:16,time:0,shade:.3}); - bt_file.file_drop.isUpload = true; - if(items && items.length && items[0].webkitGetAsEntry != null) { - if(items[0].kind != 'file') return false; - } - if(bt_file.file_drop.filesList == null) bt_file.file_drop.filesList = [] - for(var i = bt_file.file_drop.filesList.length -1; i >= 0 ; i--){ - if(bt_file.file_drop.filesList[i].is_upload) bt_file.file_drop.filesList.splice(-i,1) - } - $('#mask_layer').hide(); - function update_sync(s){ - s.getFilesAndDirectories().then(function(subFilesAndDirs) { - return iterateFilesAndDirs(subFilesAndDirs, s.path); - }); - } - - var iterateFilesAndDirs = function(filesAndDirs, path) { - if(!bt_file.file_drop.isUpload) return false - for (var i = 0; i < filesAndDirs.length; i++) { - if (typeof(filesAndDirs[i].getFilesAndDirectories) == 'function') { - update_sync(filesAndDirs[i]) - } else { - if(num > bt_file.file_drop.isUploadNumber){ - bt_file.file_drop.isUpload = false; - layer.msg(' '+ bt_file.file_drop.isUploadNumber +' items cannot upload, please compress first!。',{icon:2,area:'405px'}); - bt_file.file_drop.filesList = []; - clearTimeout(time); - return false; - } - bt_file.file_drop.filesList.push({ - file:filesAndDirs[i], - path:bt.get_file_path(path +'/'+ filesAndDirs[i].name).replace('//','/'), - name:filesAndDirs[i].name.replace('//','/'), - icon:bt_file.get_ext_name(filesAndDirs[i].name), - size:bt_file.file_drop.to_size(filesAndDirs[i].size), - upload:0, //上传状态,未上传:0、上传中:1,已上传:2,上传失败:-1 - is_upload:false - }); - bt_file.file_drop.uploadAllSize += filesAndDirs[i].size - clearTimeout(time); - time = setTimeout(function(){ - layer.close(loadT); - bt_file.file_drop.dialog_view(); - },100); - num ++; - } - } - } - if('getFilesAndDirectories' in e.dataTransfer){ - e.dataTransfer.getFilesAndDirectories().then(function(filesAndDirs) { - return iterateFilesAndDirs(filesAndDirs, '/'); - }); - } - - }, - // 上传视图 - dialog_view:function(config){ - var that = this,html = ''; - this.f_path = bt_file.file_path; - if(!$('.file_dir_uploads').length > 0){ - if(that.filesList == null) that.filesList = [] - for(var i =0; i'+ (item.path + '/' + item.name).replace('//','/') +''+ item.size +''+ that.is_upload_status(item.upload) +'
    '; - } - var is_show = that.filesList.length > 11; - layer.open({ - type: 1, - closeBtn: 1, - maxmin:true, - area: ['550px','505px'], - btn:['Upload','Cancel','Clear'], - title: 'Upload files to【'+ bt.get_cookie('Path') +'】--- Support breakpoint renewal', - skin:'file_dir_uploads', - content:'\ -
    \ -
    \ -
    \ - \ - \ - \ -
    \ - \ -
    \ -
    '+ (html!=''?('
    • File nameFile sizeFile status
      '+ html +'
    ') :'Please drag the file here'+ (!that.is_webkit?'The current browser does not support drag upload. Commend to use Chrome browser or WebKit kernel for browsing':'') +'') +'\ -
    \ -
    \ - ', - success:function(layers){ - $('#mask_layer').hide(); - layers.find('.layui-layer-btn2').css('float', 'left'); - $('.file_dir_uploads .layui-layer-max').hide(); - $('.upload_btn_groud .upload_file_btn').click(function(){$('.upload_btn_groud .dropdown-menu [data-type=file]').click()}); - $('.upload_btn_groud .dropdown-menu a').click(function(){ - var type = $(this).attr('data-type'); - $('').change(function(e){ - var files = e.target.files,arry = []; - for(var i=0;iPlease drag the file here') - $('.file_upload_info').css('display','none').siblings().css('display','inline-block'); - that.filesList.length = 0 - }); - return false; - } else { - $('.upload_file_body').addClass('active').html('Please drag the file here'); - that.filesList.length = 0; - $('.file_upload_info').css('display','none').siblings().css('display','inline-block') - return false; - } - }, - end:function (){ - // GetFiles(bt.get_cookie('Path')); - that.clear_drop_stauts(true); - }, - min:function(){ - $('.file_dir_uploads .layui-layer-max').show(); - $('#layui-layer-shade'+$('.file_dir_uploads').attr('times')).fadeOut(); - }, - restore:function(){ - $('.file_dir_uploads .layui-layer-max').hide(); - $('#layui-layer-shade'+$('.file_dir_uploads').attr('times')).fadeIn(); - } - }); - }else{ - if(config == undefined && !that.isLayuiDrop) return false; - if(that.isLayuiDrop) config = that.filesList; - $('.upload_file_body').html('
    • File nameFile sizeFile status
      ').removeClass('active'); - if(Array.isArray(config)){ - for(var i =0; i'+ (item.path + '/' + item.name).replace('//','/') +''+ item.size +''+ that.is_upload_status(item.upload) +'
      '; - } - $('.dropUpLoadFile').append(html); - }else{ - $('.dropUpLoadFile').append('
    • '+ (config.path + '/' + config.name).replace('//','/') +''+ config.size +''+ that.is_upload_status(config.upload) +'
    • '); - } - - } - }, - // 上传单文件状态 - is_upload_status:function(status,val){ - if(val === undefined) val = '' - switch(status){ - case -1: - return 'Fail'+ (val != ''?','+val:'') +''; - break; - case 0: - return 'Waiting to upload'; - break; - case 1: - return 'Uploaded'; - break; - case 2: - return 'Uploading '+ val+''; - break; - case 3: - return 'Stoped'; - break; - } - }, - // 设置上传实时反馈视图 - set_upload_view:function(index,config){ - var item = $('.dropUpLoadFile li:eq('+ index +')'),that = this; - var file_info = $('.file_upload_info'); - if($('.file_upload_info .uploadProgress').length == 0){ - $('.file_upload_info').html('Total process ,Uploading ,Fail Speed Getting,Expect time Getting'); - } - file_info.show().prev().hide().parent().css('paddingRight',0); - if(that.errorLength > 0) file_info.find('.uploadError').text('('+ that.errorLength +'份)').parent().show(); - file_info.find('.uploadNumber').html('('+ that.uploadLength +'/'+ that.filesList.length +')'); - file_info.find('.uploadProgress').html( ((that.uploadedSize / that.uploadAllSize) * 100).toFixed(2) +'%'); - if(config.upload === 1 || config.upload === -1){ - that.filesList[index].is_upload = true; - that.uploadLength += 1; - item.find('.fileLoading').css({'width':'100%','opacity':'.5','background': config.upload == -1?'#ffadad':'#20a53a21'}); - item.find('.filesize').text(config.size); - item.find('.fileStatus').html(that.is_upload_status(config.upload,(config.upload === 1?('(Time:'+ that.diff_time(that.startTime,that.endTime) +')'):config.errorMsg))); - item.find('.fileLoading').fadeOut(500,function(){ - $(this).remove(); - var uploadHeight = $('.dropUpLoadFile'); - if(uploadHeight.length == 0) return false; - if(uploadHeight[0].scrollHeight > uploadHeight.height()){ - uploadHeight.scrollTop(uploadHeight.scrollTop()+40); - } - }); - }else{ - item.find('.fileLoading').css('width',config.percent); - item.find('.filesize').text(config.upload_size +'/'+ config.size); - item.find('.fileStatus').html(that.is_upload_status(config.upload,'('+ config.percent +')')); - } - }, - // 清除上传状态 - clear_drop_stauts:function(status){ - var time = new Date(),that = this; - if(!status){ - try { - var s_peed = bt_file.file_drop.to_size(bt_file.file_drop.uploadedSize / ((time.getTime() - bt_file.file_drop.initTimer.getTime()) / 1000)) - $('.file_upload_info').html(''+ this.uploadLength +' uploaded,'+ (this.errorLength>0?(this.errorLength +'failures, '):'') +'time'+ this.diff_time(this.initTimer,time) + ',speed '+ s_peed +'/s').append($('').click(function(){ - $('.file_upload_info').hide().prev().show(); - })); - } catch (e) { - - } - } - $('.layui-layer-btn0').removeAttr('style data-upload').text(lan.upload.upload); - $.extend(bt_file.file_drop,{ - startTime: 0, - endTime:0, - uploadLength:0, //上传数量 - splitSize: 1024 * 1024 * 2, //文件上传分片大小 - filesList:[], // 文件列表数组 - errorLength:0, //上传失败文件数量 - isUpload:false, //上传状态,是否可以上传 - isUploadNumber:800,//限制单次上传数量 - uploadAllSize:0, // 上传文件总大小 - uploadedSize:0, // 已上传文件大小 - topUploadedSize:0, // 上一次文件上传大小 - uploadExpectTime:0, // 预计上传时间 - initTimer:0, // 初始化计时 - speedInterval:null, //平局速度定时器 - timerSpeed:0, //速度 - uploading:false - }); - clearInterval(that.speedInterval); - }, - // 上传文件,文件开始字段,文件编号 - upload_file:function(fileStart,index){ - if(fileStart == undefined && this.uploadSuspend.length == 0) fileStart = 0,index = 0; - if(this.filesList.length === index){ - clearInterval(this.speedInterval); - this.clear_drop_stauts(); - bt_file.reader_file_list({path:bt_file.file_path,is_operating:false}); - return false; - } - var that = this; - that.splitEndTime = new Date().getTime() - that.get_timer_speed() - - that.splitStartTime = new Date().getTime() - var item = this.filesList[index],fileEnd = ''; - if(item == undefined) return false; - fileEnd = Math.min(item.file.size, fileStart + this.splitSize), - that.fileSize = fileEnd - fileStart - form = new FormData(); - if(fileStart == 0){ - that.startTime = new Date(); - item = $.extend(item,{percent:'0%',upload:2,upload_size:'0B'}); - } - form.append("f_path", this.f_path + item.path); - form.append("f_name", item.name); - form.append("f_size", item.file.size); - form.append("f_start", fileStart); - form.append("blob", item.file.slice(fileStart, fileEnd)); - that.set_upload_view(index,item); - $.ajax({ - url:'/files?action=upload', - type: "POST", - data: form, - async: true, - processData: false, - contentType: false, - success:function(data){ - if(typeof(data) === "number"){ - that.set_upload_view(index,$.extend(item,{percent:(((data / item.file.size)* 100).toFixed(2) +'%'),upload:2,upload_size:that.to_size(data)})); - if(fileEnd != data){ - that.uploadedSize += data; - }else{ - that.uploadedSize += parseInt(fileEnd - fileStart); - } - - that.upload_file(data,index); - }else{ - if(data.status){ - that.endTime = new Date(); - that.uploadedSize += parseInt(fileEnd - fileStart); - that.set_upload_view(index,$.extend(item,{upload:1,upload_size:item.size})); - that.upload_file(0,index += 1); - }else{ - that.set_upload_view(index,$.extend(item,{upload:-1,errorMsg:data.msg})); - that.errorLength ++; - } - } - - }, - error:function(e){ - if(that.filesList[index].req_error === undefined) that.filesList[index].req_error = 1 - if(that.filesList[index].req_error > 2){ - that.set_upload_view(index,$.extend(that.filesList[index],{upload:-1,errorMsg:e.statusText == 'error'?lan.public.network_err:e.statusText })); - that.errorLength ++; - that.upload_file(fileStart,index += 1) - return false; - } - that.filesList[index].req_error += 1; - that.upload_file(fileStart,index) - - - } - }); - }, - // 获取上传速度 - get_timer_speed:function(speed){ - var done_time = new Date().getTime() - if(done_time - this.speedLastTime > 1000){ - var that = this,num = 0; - if(speed == undefined) speed = 200 - var s_time = (that.splitEndTime - that.splitStartTime) / 1000; - that.timerSpeed = (that.fileSize / s_time).toFixed(2) - that.updateedSizeLast = that.uploadedSize - if(that.timerSpeed < 2) return; - - $('.file_upload_info .uploadSpeed').text(that.to_size(isNaN(that.timerSpeed)?0:that.timerSpeed)+'/s'); - var estimateTime = that.time(parseInt(((that.uploadAllSize - that.uploadedSize) / that.timerSpeed) * 1000)) - if(!isNaN(that.timerSpeed)) $('.file_upload_info .uploadEstimate').text(estimateTime.indexOf('NaN') == -1?estimateTime:'0 '+lan.bt.s); - this.speedLastTime = done_time; - } - }, - time:function(date){ - var hours = Math.floor(date / (60 * 60 * 1000)); - var minutes = Math.floor(date / (60 * 1000)); - var seconds = parseInt((date % (60 * 1000)) / 1000); - var result = seconds + 'sec'; - if(minutes > 0) { - result = minutes + "min" + seconds + 'sec'; - } - if(hours > 0){ - result = hours + 'hour' + Math.floor((date - (hours * (60 * 60 * 1000))) / (60 * 1000)) + "min"; - } - return result - }, - diff_time: function (start_date, end_date) { - var diff = end_date.getTime() - start_date.getTime(); - var minutes = Math.floor(diff / (60 * 1000)); - var leave3 = diff % (60 * 1000); - var seconds = leave3 / 1000 - var result = seconds.toFixed(minutes > 0?0:2) + lan.bt.s; - if (minutes > 0) { - result = minutes + "min" + seconds.toFixed(0) + lan.bt.s - } - return result - }, - - to_size: function (a) { - var d = [" B", " KB", " MB", " GB", " TB", " PB"]; - var e = 1024; - for (var b = 0; b < d.length; b += 1) { - if (a < e) { - var num = (b === 0 ? a : a.toFixed(2)) + d[b]; - return (!isNaN((b === 0 ? a : a.toFixed(2))) && typeof num != 'undefined')?num:'0B'; - } - a /= e - } - } - }, init:function(){ if (bt.get_cookie('rank') == undefined || bt.get_cookie('rank') == null || bt.get_cookie('rank') == 'a' || bt.get_cookie('rank') == 'b') { bt.set_cookie('rank', 'list'); @@ -587,7 +77,7 @@ var bt_file = { this.event_bind(); // 事件绑定 this.reader_file_list({is_operating:true}); // 渲染文件列表 this.render_file_disk_list(); // 渲染文件磁盘列表 - this.file_drop.init(); // 初始化文件上传 + // this.file_drop.init(); // 初始化文件上传 this.set_file_table_width(); // 设置表格宽度 }, // 事件绑定 @@ -752,8 +242,10 @@ var bt_file = { }); }); // 上传 - $('.upload_file').on('click',function(e){ - that.file_drop.dialog_view(); + $('.upload_file').on('click',function(e) { + var path = $('#fileInputPath').attr('data-path'); + uploadFiles.init_upload_path(path); + uploadFiles.upload_layer(); }); // 下载 $('.upload_download').on('click',function(e){ @@ -1084,14 +576,24 @@ var bt_file = { //设置单页显示的数量,默认为100,设置local本地缓存 $('.filePage').on('change','.showRow',function(){ var val = $(this).val(); - bt.set_cookie('showRow',val) - that.reader_file_list({showRow:val,p:1,is_operating:false}); + bt.set_storage('local', 'showRow', val); + var search = $('.file_search_input').val(); + var data = { showRow: val, p: 1, is_operating: false, search: search, file_btn: !!search } + if ($('#search_all').hasClass('active')) { + data.all = 'True'; + } + that.reader_file_list(data); }); // 页码跳转 $('.filePage').on('click','div:nth-child(2) a',function(e){ var num = $(this).attr('href').match(/p=([0-9]+)$/)[1]; - that.reader_file_list({path:that.path,p:num}) + var search = $('.file_search_input').val(); + var data = { path: that.path, p: num, search: search, file_btn: !!search } + if ($('#search_all').hasClass('active')) { + data.all = 'True'; + } + that.reader_file_list(data); e.stopPropagation(); e.preventDefault(); }) @@ -1154,10 +656,11 @@ var bt_file = { top: endPos.top > startPos.top ? startPos.top : endPos.top, left: endPos.left > startPos.left ? startPos.left : endPos.left }; + var enter_files_box = that.enter_files_box() if(bt.get_cookie('rank') == 'list'){ //在列表模式下减去表头高度 fixedPoint.top = fixedPoint.top + 40 } - var enter_files_box = that.enter_files_box() + // 拖拽范围的宽高 var w = Math.min(Math.abs(endPos.left - startPos.left), con_l + container.width() - fixedPoint.left); var h = Math.min(Math.abs(endPos.top - startPos.top), con_t + container.height() - fixedPoint.top); @@ -1216,29 +719,31 @@ var bt_file = { } // 鼠标抬起 - bt_file.window_mouseup = function() { + bt_file.window_mouseup = function(ev) { var _move_array = [],enter_files_box = that.enter_files_box(); var box_offset_top = enter_files_box.offset().top; var box_offset_left = enter_files_box.offset().left; var box_offset_w = enter_files_box.offset().left + enter_files_box.width(); var box_offset_h = enter_files_box.offset().top + enter_files_box.height(); - $(container).find('.file_tr').each(function(i,item){ - var offset_top = $(item).offset().top; - var offset_left = $(item).offset().left; - var offset_h = $(item).offset().top + $(item).height(); - var offset_w = $(item).offset().left + $(item).width(); + if(box_offset_top && box_offset_left && box_offset_w && box_offset_h){ + $(container).find('.file_tr').each(function(i,item){ + var offset_top = $(item).offset().top; + var offset_left = $(item).offset().left; + var offset_h = $(item).offset().top + $(item).height(); + var offset_w = $(item).offset().left + $(item).width(); - if(bt.get_cookie('rank') == 'icon'){ // 为Icon模式时 - if(offset_w >= box_offset_left && offset_left <= box_offset_w && offset_h >= box_offset_top && offset_top <= box_offset_h){ - _move_array.push($(item).data('index')) - } - }else{// 为List模式时 - if (offset_w >= box_offset_left && offset_h >= box_offset_top && offset_top <= box_offset_h ) { - _move_array.push($(item).data('index')) - } - } - }); - that.render_file_selected(_move_array); //渲染数据 + if(bt.get_cookie('rank') == 'icon'){ // 为Icon模式时 + if(offset_w >= box_offset_left && offset_left <= box_offset_w && offset_h >= box_offset_top && offset_top <= box_offset_h){ + _move_array.push($(item).data('index')) + } + }else{// 为List模式时 + if (offset_w >= box_offset_left && offset_h >= box_offset_top && offset_top <= box_offset_h ) { + _move_array.push($(item).data('index')) + } + } + }); + that.render_file_selected(_move_array); //渲染数据 + } enter_files_box.remove(); //删除盒子 $('.file_list_content').unbind('mousewheel'); //解绑滚轮事件 $(document).unbind('mousemove',bt_file.window_mousemove); @@ -1292,24 +797,31 @@ var bt_file = { if(type == 'more') return true; item.open = type; item.index = data.index; - item.type_tips = item.type == 'file'?'File':'Directory'; + item.type_tips = item.type == 'file' ? 'File' : 'Directory'; that.file_groud_event(item); }); // 文件搜索 - $('.replace_content').on('click', function() { + $('.replace_content').on('click', function () { that.replace_content_view() }) }, + // 上传文件 + file_drop: function () { + var path = $('#fileInputPath').attr('data-path'); + uploadFiles.init_upload_path(path); + uploadFiles.upload_layer(); + }, /** * @descripttion: 文件拖拽范围 * @author: Lifu * @return: 拖拽元素 */ - enter_files_box:function(){ - if($('#web_mouseDrag').length == 0){ - $('
      ',{id:'web_mouseDrag', style: [ - 'position:absolute; top:0; left:0;', - 'border:1px solid #072246; background-color: #cce8ff;', + enter_files_box: function () { + if ($('#web_mouseDrag').length == 0) { + $('
      ', { + id: 'web_mouseDrag', style: [ + 'position:absolute; top:0; left:0;', + 'border:1px solid #072246; background-color: #cce8ff;', 'filter:Alpha(Opacity=15); opacity:0.15;', 'overflow:hidden;display:none;z-index:9;' ].join('')}).appendTo('.file_table_view'); @@ -1443,12 +955,12 @@ var bt_file = { }); _width += $('.menu-header-foot').innerWidth(); if(menu_width - _width < (disk_list_width+5)){ - $('.nav_group.mount_disk_list').addClass('thezoom').find('.disk_title_group_btn').removeClass('hide'); + $('.nav_group.mount_disk_list').addClass('thezoom').find('.disk_title_group_btn').removeClass('hide'); }else{ $('.nav_group.mount_disk_list,.nav_group.multi').removeClass('thezoom'); } - if(this.area[0] < 1700){ - indexs = Math.ceil(((1700 - this.area[0]) / 68)); + if(this.area[0] < 1760){ + indexs = Math.ceil(((1760 - this.area[0]) / 68)); $('.batch_group_list>.nav_btn_group').each(function(index){ if(index >= $('.batch_group_list>.nav_btn_group').length - (indexs+2)){ $(this).hide(); @@ -2017,13 +1529,13 @@ var bt_file = { '
      ' + (item.is_os_ps?item.ps:'') + '
      '+ '
      '+ ''+ is_editor_tips +' | '+ - 'Copy | '+ - 'Cut | '+ - 'Rename | '+ - 'PMSN | '+ + 'Copy | '+ + 'Cut | '+ + 'Rename | '+ + 'PMSN | '+ ''+ (is_compress?'Unzip':'Zip') +' | '+ - 'Del | '+ - 'More'+ + 'Del | '+ + 'More'+ '
      '+ ''; if(item.type == 'dir') is_dir_num ++; @@ -2123,7 +1635,8 @@ var bt_file = { delete config['cancel_favorites']; // 未分享 config['favorites'] = (data.type == 'dir' ? 'Favorites dir' : 'Favorites file'); } - if (data.ext == 'php') config['dir_kill'] = '文件查杀'; + // if (data.ext == 'php') config['dir_kill'] = '文件查杀'; + if (data.ext == 'php') delete config['dir_kill']; if (data.ext != 'php' && data.type != 'dir') delete config['dir_kill']; var num = 0; $.each(compression, function(index, item) { // 判断压缩文件 @@ -2411,15 +1924,17 @@ var bt_file = { case 'folad': //解压到... this.unpack_file_to_path(data) break; - case 'refresh': // 刷新文件列表 - $('.file_path_refresh').click(); - break; - case 'upload': //上传文件 - this.file_drop.dialog_view(); - break; - case 'soft_link': //软链接创建 - this.set_soft_link(); - break; + case 'refresh': // 刷新文件列表 + $('.file_path_refresh').click(); + break; + case 'upload': //上传文件 + var path = $('#fileInputPath').attr('data-path'); + uploadFiles.init_upload_path(path); + uploadFiles.upload_layer(); + break; + case 'soft_link': //软链接创建 + this.set_soft_link(); + break; case 'create_dir': // 新建文件目录 $('.file_nav_view .create_file_or_dir li').eq(0).click(); break; @@ -2449,9 +1964,17 @@ var bt_file = { * @return: 无返回值 */ batch_file_manage:function(stype){ - var that = this,_api = '',_fname = [],_obj = {},_path = $(''); + var that = this, + _api = '', + _fname = [], + _obj = {}, + _path = $('') + types = []; $.each(this.file_table_arry,function(index,item){ - _fname.push(item.filename) + if (item.type && types.indexOf(item.type) == -1) { + types.push(item.type); + } + _fname.push(item.filename); }) switch(stype){ case 'copy': //复制 @@ -2479,8 +2002,16 @@ var bt_file = { _obj['filename'] = _fname.join(','); _obj['open'] = 'tar_gz' _obj['path'] = that.file_path+'/'+file_title; + if (types.length > 1) { + _obj['type_tips'] = 'folder and file'; + } else if (types[0] == 'dir') { + _obj['type_tips'] = 'folder'; + } else if (types[0] == 'file') { + _obj['type_tips'] = 'file'; + } else { + _obj['type_tips'] = ''; + } return that.compress_file_or_dir(_obj,true) - break; } // 批量标记 that.$http(_api,_obj,function(res){ @@ -4210,8 +3741,7 @@ var bt_file = { $('.layer_close').click(function () { layer.close(layers); }); - - that.$http('get_path_premissions',{path: data.filename},function(edata){ + that.$http('get_path_premissions',{path: data.path},function(edata){ if(edata.length == 0) $('.backuptip').text('No backup'); }); $('button.restore').click(function () { @@ -4579,14 +4109,19 @@ var bt_file = { */ remove_present_task:function(id){ var that = this; - layer.confirm('Do you want to cancel the upload of files? It need to delete the uploaded files manually. Continue?',{title:'Cancel file upload',icon:0},function(indexs){ - bt.send('remove_task','task/remove_task',{id:id},function(rdata){ - layer.msg(rdata.msg,{icon:1}) - layer.close(that.file_present_task) - that.file_present_task = null; - }) - layer.close(indexs); - }); + layer.confirm('Do you want to cancel the current task queue?', { + title: 'Cancel task queue', + icon: 0 + }, function (indexs) { + layer.close(indexs); + var loadT = bt.load('Canceling task...'); + $.post('/task?action=remove_task', { + id: id + }, function(rdata) { + loadT.close() + bt.msg(rdata); + }); + }); }, /** * @descripttion 设置访问权限 diff --git a/BTPanel/static/js/files.min.js b/BTPanel/static/js/files.min.js index 4ba0e407..6c89acf7 100644 --- a/BTPanel/static/js/files.min.js +++ b/BTPanel/static/js/files.min.js @@ -1 +1 @@ -var bt_file={area:[],loadT:null,loadY:null,vscode:null,file_table_arry:[],timerM:null,is_update_down_list:!0,is_recycle:!1,is_editor:!1,file_header:{file_checkbox:40,file_name:"auto",file_accept:130,file_size:90,file_mtime:155,file_ps:"auto",file_operation:350,file_tr:0,file_list_header:0},file_operating:[],file_pointer:-1,file_path:"C:/",file_page_num:bt.get_storage("local","showRow")||100,file_list:[],file_store_list:[],file_store_current:0,file_images_list:[],file_drop:null,file_present_task:null,file_selection_operating:[],file_share_list:[],scroll_width:function(){var noScroll,scroll,oDiv=document.createElement("DIV");return oDiv.style.cssText="position:absolute; top:-1000px; width:100px; height:100px; overflow:hidden;",noScroll=document.body.appendChild(oDiv).clientWidth,oDiv.style.overflowY="scroll",scroll=oDiv.clientWidth,document.body.removeChild(oDiv),noScroll-scroll}(),is_mobile:function(){return!(!navigator.userAgent.match(/mobile/i)&&!/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))&&(layer.msg(navigator.userAgent.match(/mobile/i),{icon:2,time:0}),!0)},method_list:{GetFileBody:lan.public.get_file_contents,DeleteDir:lan.public.deleting,DeleteFile:lan.public.deleting,GetDiskInfo:["system",lan.public.getting_disk_list],CheckExistsFiles:lan.files.check_same_name_file,GetFileAccess:lan.files.getting_permission_list,SetFileAccess:lan.public.config,DelFileAccess:lan.files.deleting_user,get_path_size:lan.files.getting_dir_size,add_files_store_types:lan.files.create_favorite,get_files_store:lan.files.getting_favorite,del_files_store:lan.files.unfavorite,dir_webshell_check:"目录查杀",file_webshell_check:"查杀文件",get_download_url_list:lan.files.getting_share_list,get_download_url_find:lan.files.getting_s_share_info,create_download_url:lan.files.create_share,remove_download_url:lan.files.cancel_share,add_files_store:lan.files.adding_favorite,CopyFile:lan.files.copy_the,MvFile:lan.files.mv_the,SetBatchData:lan.files.performing_bulk_opt,Get_Recycle_bin:lan.files.getting_recycle_list,get_path_premissions:lan.files.getting_permission_list,back_path_permissions:lan.files.setting_permission_list,get_all_back:lan.files.getting_permission_list,fix_permissions:lan.files.fix_permission,del_path_premissions:lan.files.deleting_permission},file_drop:{f_path:null,startTime:0,endTime:0,uploadLength:0,splitSize:2097152,splitEndTime:0,splitStartTime:0,fileSize:0,speedLastTime:0,filesList:[],errorLength:0,isUpload:!0,uploadSuspend:[],isUploadNumber:800,uploadAllSize:0,uploadedSize:0,updateedSizeLast:0,topUploadedSize:0,uploadExpectTime:0,initTimer:0,speedInterval:null,timerSpeed:0,isLayuiDrop:!1,uploading:!1,is_webkit:navigator.userAgent.indexOf("WebKit")>-1,init:function(){0==$("#mask_layer").length&&(window.UploadFiles=function(){bt_file.file_drop.dialog_view()},$("body").append($('")),this.event_relation(document.querySelector("#container"),document,document.querySelector("#mask_layer")))},event_relation:function(enter,leave,drop){var that=this,obj=Object.keys(arguments);for(var item in arguments)"object"==typeof arguments[item]&&void 0!==arguments[item].nodeType&&(arguments[item]={el:arguments[item],callback:null});leave.el.addEventListener("dragleave",null!=leave.callback?leave.callback:function(e){0==e.x&&0==e.y&&$("#mask_layer").hide(),e.preventDefault()},!1),enter.el.addEventListener("dragenter",null!=enter.callback?enter.callback:function(e){if("string"==e.dataTransfer.items[0].kind)return!1;$("#mask_layer").show(),that.isLayuiDrop=!1,e.preventDefault()},!1),drop.el.addEventListener("dragover",(function(e){e.preventDefault()}),!1),drop.el.addEventListener("drop",null!=enter.callback?drop.callback:that.ev_drop,!1)},ev_drop:function(e){if("string"==e.dataTransfer.items[0].kind)return!1;if(!bt_file.file_drop.is_webkit)return $("#mask_layer").hide(),!1;if(e.preventDefault(),bt_file.file_drop.uploading)return layer.msg("Uploading files, please wait..."),!1;var items=e.dataTransfer.items,time,num=0;if(loadT=layer.msg("Getting upload files details, please wait...",{icon:16,time:0,shade:.3}),bt_file.file_drop.isUpload=!0,items&&items.length&&null!=items[0].webkitGetAsEntry&&"file"!=items[0].kind)return!1;null==bt_file.file_drop.filesList&&(bt_file.file_drop.filesList=[]);for(var i=bt_file.file_drop.filesList.length-1;i>=0;i--)bt_file.file_drop.filesList[i].is_upload&&bt_file.file_drop.filesList.splice(-i,1);function update_sync(s){s.getFilesAndDirectories().then((function(subFilesAndDirs){return iterateFilesAndDirs(subFilesAndDirs,s.path)}))}$("#mask_layer").hide();var iterateFilesAndDirs=function(filesAndDirs,path){if(!bt_file.file_drop.isUpload)return!1;for(var i=0;ibt_file.file_drop.isUploadNumber)return bt_file.file_drop.isUpload=!1,layer.msg(" "+bt_file.file_drop.isUploadNumber+" items cannot upload, please compress first!。",{icon:2,area:"405px"}),bt_file.file_drop.filesList=[],clearTimeout(time),!1;bt_file.file_drop.filesList.push({file:filesAndDirs[i],path:bt.get_file_path(path+"/"+filesAndDirs[i].name).replace("//","/"),name:filesAndDirs[i].name.replace("//","/"),icon:bt_file.get_ext_name(filesAndDirs[i].name),size:bt_file.file_drop.to_size(filesAndDirs[i].size),upload:0,is_upload:!1}),bt_file.file_drop.uploadAllSize+=filesAndDirs[i].size,clearTimeout(time),time=setTimeout((function(){layer.close(loadT),bt_file.file_drop.dialog_view()}),100),num++}};"getFilesAndDirectories"in e.dataTransfer&&e.dataTransfer.getFilesAndDirectories().then((function(filesAndDirs){return iterateFilesAndDirs(filesAndDirs,"/")}))},dialog_view:function(config){var that=this,html="";if(this.f_path=bt_file.file_path,!$(".file_dir_uploads").length>0){null==that.filesList&&(that.filesList=[]);for(var i=0;i'+(item.path+"/"+item.name).replace("//","/")+''+item.size+''+that.is_upload_status(item.upload)+'
      '}var is_show=that.filesList.length>11;layer.open({type:1,closeBtn:1,maxmin:!0,area:["550px","505px"],btn:["Upload","Cancel","Clear"],title:"Upload files to【"+bt.get_cookie("Path")+"】--- Support breakpoint renewal",skin:"file_dir_uploads",content:'
      '+(""!=html?'
      • File nameFile sizeFile status
        '+html+"
      ":"Please drag the file here"+(that.is_webkit?"":'The current browser does not support drag upload. Commend to use Chrome browser or WebKit kernel for browsing')+"")+"
      ",success:function(layers){$("#mask_layer").hide(),layers.find(".layui-layer-btn2").css("margin","0 300px 0 0"),$(".file_dir_uploads .layui-layer-max").hide(),$(".upload_btn_groud .upload_file_btn").click((function(){$(".upload_btn_groud .dropdown-menu [data-type=file]").click()})),$(".upload_btn_groud .dropdown-menu a").click((function(){var type=$(this).attr("data-type");$('").change((function(e){for(var files=e.target.files,arry=[],i=0;iPlease drag the file here"),$('.file_upload_info').css('display','none').siblings().css('display','block'),that.filesList.length=0})),!1):($(".upload_file_body").addClass("active").html("Please drag the file here"),$('.file_upload_info').css('display','none').siblings().css('display','block'),that.filesList.length=0,!1)},end:function(){that.clear_drop_stauts(!0)},min:function(){$(".file_dir_uploads .layui-layer-max").show(),$("#layui-layer-shade"+$(".file_dir_uploads").attr("times")).fadeOut()},restore:function(){$(".file_dir_uploads .layui-layer-max").hide(),$("#layui-layer-shade"+$(".file_dir_uploads").attr("times")).fadeIn()}})}else{if(null==config&&!that.isLayuiDrop)return!1;if(that.isLayuiDrop&&(config=that.filesList),$(".upload_file_body").html('
      • File nameFile sizeFile status
        ').removeClass("active"),Array.isArray(config)){for(var i=0;i'+(item.path+"/"+item.name).replace("//","/")+''+item.size+''+that.is_upload_status(item.upload)+'
        '}$(".dropUpLoadFile").append(html)}else $(".dropUpLoadFile").append('
      • '+(config.path+"/"+config.name).replace("//","/")+''+config.size+''+that.is_upload_status(config.upload)+'
      • ')}},is_upload_status:function(status,val){switch(void 0===val&&(val=""),status){case-1:return'Fail'+(""!=val?","+val:"")+"";case 0:return'Waiting to upload';case 1:return'Uploaded';case 2:return'Uploading '+val+"";case 3:return'Stoped'}},set_upload_view:function(index,config){var item=$(".dropUpLoadFile li:eq("+index+")"),that=this,file_info=$(".file_upload_info");0==$(".file_upload_info .uploadProgress").length&&$(".file_upload_info").html('Total process ,Uploading ,Fail Speed Getting,Expect time Getting'),file_info.show().prev().hide().parent().css("paddingRight",0),this.errorLength>0&&file_info.find(".uploadError").text("("+this.errorLength+"份)").parent().show(),file_info.find(".uploadNumber").html("("+this.uploadLength+"/"+this.filesList.length+")"),file_info.find(".uploadProgress").html((this.uploadedSize/this.uploadAllSize*100).toFixed(2)+"%"),1===config.upload||-1===config.upload?(this.filesList[index].is_upload=!0,this.uploadLength+=1,item.find(".fileLoading").css({width:"100%",opacity:".5",background:-1==config.upload?"#ffadad":"#20a53a21"}),item.find(".filesize").text(config.size),item.find(".fileStatus").html(this.is_upload_status(config.upload,1===config.upload?"(Time:"+this.diff_time(this.startTime,this.endTime)+")":config.errorMsg)),item.find(".fileLoading").fadeOut(500,(function(){$(this).remove();var uploadHeight=$(".dropUpLoadFile");if(0==uploadHeight.length)return!1;uploadHeight[0].scrollHeight>uploadHeight.height()&&uploadHeight.scrollTop(uploadHeight.scrollTop()+40)}))):(item.find(".fileLoading").css("width",config.percent),item.find(".filesize").text(config.upload_size+"/"+config.size),item.find(".fileStatus").html(this.is_upload_status(config.upload,"("+config.percent+")")))},clear_drop_stauts:function(status){var time=new Date,that=this;if(!status)try{var s_peed=bt_file.file_drop.to_size(bt_file.file_drop.uploadedSize/((time.getTime()-bt_file.file_drop.initTimer.getTime())/1e3));$(".file_upload_info").html(""+this.uploadLength+" uploaded,"+(this.errorLength>0?this.errorLength+"failures, ":"")+"time"+this.diff_time(this.initTimer,time)+",speed "+s_peed+"/s").append($('').click((function(){$(".file_upload_info").hide().prev().show()})))}catch(e){}$(".layui-layer-btn0").removeAttr("style data-upload").text(lan.upload.upload),$.extend(bt_file.file_drop,{startTime:0,endTime:0,uploadLength:0,splitSize:2097152,filesList:[],errorLength:0,isUpload:!1,isUploadNumber:800,uploadAllSize:0,uploadedSize:0,topUploadedSize:0,uploadExpectTime:0,initTimer:0,speedInterval:null,timerSpeed:0,uploading:!1}),clearInterval(this.speedInterval)},upload_file:function(fileStart,index){if(null==fileStart&&0==this.uploadSuspend.length&&(fileStart=0,index=0),this.filesList.length===index)return clearInterval(this.speedInterval),this.clear_drop_stauts(),bt_file.reader_file_list({path:bt_file.file_path,is_operating:!1}),!1;var that=this;that.splitEndTime=(new Date).getTime(),that.get_timer_speed(),that.splitStartTime=(new Date).getTime();var item=this.filesList[index],fileEnd="";if(null==item)return!1;fileEnd=Math.min(item.file.size,fileStart+this.splitSize),that.fileSize=fileEnd-fileStart,form=new FormData,0==fileStart&&(that.startTime=new Date,item=$.extend(item,{percent:"0%",upload:2,upload_size:"0B"})),form.append("f_path",this.f_path+item.path),form.append("f_name",item.name),form.append("f_size",item.file.size),form.append("f_start",fileStart),form.append("blob",item.file.slice(fileStart,fileEnd)),that.set_upload_view(index,item),$.ajax({url:"/files?action=upload",type:"POST",data:form,async:!0,processData:!1,contentType:!1,success:function(data){"number"==typeof data?(that.set_upload_view(index,$.extend(item,{percent:(data/item.file.size*100).toFixed(2)+"%",upload:2,upload_size:that.to_size(data)})),that.uploadedSize+=fileEnd!=data?data:parseInt(fileEnd-fileStart),that.upload_file(data,index)):data.status?(that.endTime=new Date,that.uploadedSize+=parseInt(fileEnd-fileStart),that.set_upload_view(index,$.extend(item,{upload:1,upload_size:item.size})),that.upload_file(0,index+=1)):(that.set_upload_view(index,$.extend(item,{upload:-1,errorMsg:data.msg})),that.errorLength++)},error:function(e){if(void 0===that.filesList[index].req_error&&(that.filesList[index].req_error=1),that.filesList[index].req_error>2)return that.set_upload_view(index,$.extend(that.filesList[index],{upload:-1,errorMsg:"error"==e.statusText?lan.public.network_err:e.statusText})),that.errorLength++,that.upload_file(fileStart,index+=1),!1;that.filesList[index].req_error+=1,that.upload_file(fileStart,index)}})},get_timer_speed:function(speed){var done_time=(new Date).getTime();if(done_time-this.speedLastTime>1e3){var that=this,num=0;null==speed&&(speed=200);var s_time=(this.splitEndTime-this.splitStartTime)/1e3;if(this.timerSpeed=(this.fileSize/s_time).toFixed(2),this.updateedSizeLast=this.uploadedSize,this.timerSpeed<2)return;$(".file_upload_info .uploadSpeed").text(this.to_size(isNaN(this.timerSpeed)?0:this.timerSpeed)+"/s");var estimateTime=this.time(parseInt((this.uploadAllSize-this.uploadedSize)/this.timerSpeed*1e3));isNaN(this.timerSpeed)||$(".file_upload_info .uploadEstimate").text(-1==estimateTime.indexOf("NaN")?estimateTime:"0 "+lan.bt.s),this.speedLastTime=done_time}},time:function(date){var hours=Math.floor(date/36e5),minutes=Math.floor(date/6e4),seconds=parseInt(date%6e4/1e3),result=seconds+"sec";return minutes>0&&(result=minutes+"min"+seconds+"sec"),hours>0&&(result=hours+"hour"+Math.floor((date-36e5*hours)/6e4)+"min"),result},diff_time:function(start_date,end_date){var diff=end_date.getTime()-start_date.getTime(),minutes=Math.floor(diff/6e4),leave3,seconds=diff%6e4/1e3,result=seconds.toFixed(minutes>0?0:2)+lan.bt.s;return minutes>0&&(result=minutes+"min"+seconds.toFixed(0)+lan.bt.s),result},to_size:function(a){for(var d=[" B"," KB"," MB"," GB"," TB"," PB"],e=1024,b=0;b1)return that.file_groud_event({open:"soft_link"}),!1;var type=$(this).data("type"),nav_down_list=$(".create_file_or_dir .nav_down_list");if(nav_down_list.css({display:function(){return setTimeout((function(){nav_down_list.removeAttr("style")}),100),"none"}}),that.is_editor)return!1;that.is_editor=!0,$(".file_list_content").prepend('
        '+("icon"==bt.get_cookie("rank")?'":'')+"
        "),$(("icon"==bt.get_cookie("rank")?"textarea":"input")+"[name=createArea]").on("input",(function(){"icon"==bt.get_cookie("rank")&&(this.style.height="auto",this.style.height=this.scrollHeight+"px")})).keyup((function(e){13==e.keyCode&&$(this).blur()})).blur((function(e){var _val=$(this).val().replace(/[\r\n]/g,"");if(that.match_unqualified_string(_val))return layer.msg('Name cannot contain /\\:*?"<>| symbol',{icon:2});""==_val&&(_val="newBlankDir"==type?"New directory":"New blank file"),setTimeout((function(){that.create_file_req({type:"newBlankDir"==type?"folder":"file",path:that.file_path+"/"+_val},(function(res){res.status&&that.reader_file_list({path:that.file_path}),layer.msg(res.msg,{icon:res.status?1:2})})),$(".createModel").remove(),that.is_editor=!1}),300),e.preventDefault()})).focus(),e.stopPropagation(),e.preventDefault()})),$(".file_nav_view .favorites_file_path ul").on("click","li",(function(e){var _href=$(this).data("path"),_type=$(this).data("type"),nav_down_list=$(".favorites_file_path .nav_down_list");if("dir"==_type)that.reader_file_list({path:_href,is_operating:!0});else{if(null!=$(this).data("null"))return!1;var _file=$(this).attr("title").split("."),_fileT=_file[_file.length-1],_fileE;switch(that.determine_file_type(_fileT)){case"text":openEditorView(0,_href);break;case"video":that.open_video_play(_href);break;case"images":that.open_images_preview({filename:$(this).attr("title"),path:_href});break;default:that.reader_file_list({path:that.retrun_prev_path(_href),is_operating:!0})}}nav_down_list.css({display:function(){return setTimeout((function(){nav_down_list.removeAttr("style")}),100),"none"}}),e.stopPropagation(),e.preventDefault()})),$(".terminal_view").on("click",(function(){web_shell()})),$(".share_file_list").on("click",(function(){that.open_share_view()})),$(".mount_disk_list").on("click",".nav_btn",(function(){var path=$(this).data("menu");that.reader_file_list({path:path,is_operating:!0})})),$(".mount_disk_list").on("click",".nav_down_list li",(function(){var path=$(this).data("disk"),disk_list=$(".mount_disk_list.thezoom .nav_down_list");disk_list.css({display:function(){return setTimeout((function(){disk_list.removeAttr("style")}),100),"none"}}),that.reader_file_list({path:path,is_operating:!0})})),$(".file_nav_view").on("click",".manage_backup",(function(ev){that.manage_backup(),ev.stopPropagation(),ev.preventDefault()})),$(".file_nav_view").on("click",".recycle_bin",(function(ev){that.recycle_bin_view(),ev.stopPropagation(),ev.preventDefault()})),$(".file_nav_view .multi").on("click",".nav_btn_group",(function(ev){var batch_type=$(this).data("type");void 0!==batch_type&&that.batch_file_manage(batch_type),ev.stopPropagation(),ev.preventDefault()})),$(".file_nav_view .multi").on("click",".nav_btn_group li",(function(ev){var batch_type=$(this).data("type");that.batch_file_manage(batch_type),ev.stopPropagation(),ev.preventDefault()})),$(".file_nav_view").on("click",".file_all_paste",(function(){that.paste_file_or_dir()})),$(".file_list_header").on("click",".file_name,.file_size,.file_mtime,.file_accept,.file_user",(function(e){var _tid=$(this).attr("data-tid"),_reverse=$(this).find(".icon_sort").hasClass("active"),_active=$(this).hasClass("active");return!$(this).find(".icon_sort").hasClass("active")&&$(this).hasClass("active")?$(this).find(".icon_sort").addClass("active"):$(this).find(".icon_sort").removeClass("active"),$(this).addClass("active").siblings().removeClass("active").find(".icon_sort").removeClass("active").empty(),$(this).find(".icon_sort").html(''),_active||(_reverse=!0),bt.set_cookie("files_sort",_tid),bt.set_cookie("name_reverse",_reverse?"True":"False"),that.reader_file_list({reverse:_reverse?"True":"False",sort:_tid}),!1})),$(".file_list_header .file_th").each((function(index,item){var files_sort=bt.get_cookie("files_sort"),name_reverse=bt.get_cookie("name_reverse");$(this).attr("data-tid")===files_sort&&($(this).addClass("active").siblings().removeClass("active").find(".icon_sort").removeClass("active").empty(),$(this).find(".icon_sort").html(''),"False"===name_reverse&&$(this).find(".icon_sort").addClass("active"))})),$(".file_list_header .file_check").on("click",(function(e){var checkbox;switch(parseInt($(this).data("checkbox"))){case 0:$(this).addClass("active").removeClass("active_2").data("checkbox",1),$(".file_list_content .file_tr").addClass("active").removeClass("active_2"),$(".nav_group.multi").removeClass("hide"),$(".file_menu_tips").addClass("hide"),that.file_table_arry=that.file_list.slice();break;case 2:$(this).addClass("active").removeClass("active_2").data("checkbox",1),$(".file_list_content .file_tr").addClass("active"),$(".nav_group.multi").removeClass("hide"),$(".file_menu_tips").addClass("hide"),that.file_table_arry=that.file_list.slice();break;case 1:$(this).removeClass("active active_2").data("checkbox",0),$(".file_list_content .file_tr").removeClass("active"),$(".nav_group.multi").addClass("hide"),$(".file_menu_tips").removeClass("hide"),that.file_table_arry=[]}that.calculate_table_active()})),$(".file_list_content").on("click",".file_checkbox",(function(e){var _tr=$(this).parents(".file_tr"),index=_tr.data("index"),filename=_tr.data("filename");_tr.hasClass("active")?(_tr.removeClass("active"),that.remove_check_file(that.file_table_arry,"filename",filename)):(_tr.addClass("active"),_tr.attr("data-filename",that.file_list[index].filename),that.file_table_arry.push(that.file_list[index])),that.calculate_table_active(),e.stopPropagation()})),$(".file_list_content").scroll((function(e){$(this).scrollTop()==$(this)[0].scrollHeight-$(this)[0].clientHeight?($(this).prev().css("opacity",1),$(this).next().css("opacity",0)):$(this).scrollTop()>0?$(this).prev().css("opacity",1):0==$(this).scrollTop()&&($(this).prev().css("opacity",0),$(this).next().css("opacity",1))})),$(".file_table_view .file_list_content").on("click",".file_tr",(function(e){if($(e.target).hasClass("foo_menu_title")||$(e.target).parents().hasClass("foo_menu_title"))return!0;$(this).addClass("active").siblings().removeClass("active"),that.file_table_arry=[that.file_list[$(this).data("index")]],that.calculate_table_active(),e.stopPropagation(),e.preventDefault()})),$(".file_table_view .file_list_content").on("click",".file_name .iconfont",(function(e){var file_tr,index=$(this).parents(".file_tr").data("index"),data=that.file_list[index];data.index=index,$(this).hasClass("icon-share1")&&that.info_file_share(data),$(this).hasClass("icon-favorites")&&that.cancel_file_favorites(data),e.stopPropagation()})),$(".file_table_view .file_list_content").on("dblclick",".file_tr",(function(e){var index=$(this).data("index"),data=that.file_list[index];if($(e.target).hasClass("file_check")||$(e.target).parents(".foo_menu").length>0||$(e.target).hasClass("set_file_ps")||that.is_editor)return!1;if("dir"==data.type){if("Recycle_bin"==data.filename)return that.recycle_bin_view();that.reader_file_list({path:that.file_path+"/"+data.filename,is_operating:!0})}else switch(data.open_type){case"text":openEditorView(0,data.path);break;case"video":that.open_video_play(data);break;case"images":that.open_images_preview(data);break;case"compress":that.unpack_file_to_path(data)}e.stopPropagation(),e.preventDefault()})),$(".file_table_view .file_list_content").on("click",".file_title i,.file_ico_type .file_icon",(function(e){var file_tr,index=$(this).parents(".file_tr").data("index"),data=that.file_list[index];if("dir"==data.type){if("Recycle_bin"==data.filename)return that.recycle_bin_view();that.reader_file_list({path:that.file_path+"/"+data.filename,is_operating:!0})}else layer.msg("compress"==data.open_type?"Double click to unzip the file":"Double click to edit the file");e.stopPropagation(),e.preventDefault()})),$(".file_list_content").on("contextmenu",(function(ev){return"createArea"==$(ev.target).attr("name")||"rename_file_input"==$(ev.target).attr("name")})),$(".selection_right_menu").on("contextmenu",(function(ev){return!1})),$(".file_list_content").on("mousedown",".file_tr",(function(ev){if(1===ev.which&&($(ev.target).hasClass("foo_menu_title")||$(ev.target).parents().hasClass("foo_menu_title")))that.render_file_groud_menu(ev,this),$(ev.target).parent().addClass("foo_menu_click"),$(this).siblings().find(".foo_menu").removeClass("foo_menu_click"),$(this).addClass("active").siblings().removeClass("active");else{if(3!==ev.which||that.is_editor)return!0;that.file_table_arry.length>1?that.render_files_multi_menu(ev):(that.render_file_groud_menu(ev,this),$(".content_right_menu").removeAttr("style"),$(this).addClass("active").siblings().removeClass("active"))}ev.stopPropagation(),ev.preventDefault()})),$(".filePage").on("change",".showRow",(function(){var val=$(this).val();bt.set_cookie("showRow",val),that.reader_file_list({showRow:val,p:1,is_operating:!1})})),$(".filePage").on("click","div:nth-child(2) a",(function(e){var num=$(this).attr("href").match(/p=([0-9]+)$/)[1];that.reader_file_list({path:that.path,p:num}),e.stopPropagation(),e.preventDefault()})),$(".file_list_content").on("click",".folder_size",(function(e){var data=that.file_list[$(this).parents(".file_tr").data("index")],_this=this;that.get_file_size({path:data.path},(function(res){$(_this).text(bt.format_size(res.size))})),e.stopPropagation(),e.preventDefault()})),$(".filePage").on("click","#file_all_size",(function(e){if("/"===that.file_path)return layer.tips('The current directory is document root (/),calculate size will occupymassive server IO,continue?',this,{tips:[1,"red"],time:5e3}),!1;that.get_dir_size({path:that.file_path})})),$(".file_list_content").on("mousedown",(function(ev){if($(ev.target).hasClass("file_checkbox")||$(ev.target).hasClass("file_check")||$(ev.target).hasClass("icon-share1")||$(ev.target).hasClass("icon-favorites")||"i"==ev.target.localName||$(ev.target).parents(".app_menu_group").length>0||$(ev.target).hasClass("createModel")||$(ev.target).hasClass("editr_tr")||"createArea"==$(ev.target).attr("name")||"rename_file_input"==$(ev.target).attr("name")||$(ev.target).hasClass("set_file_ps")||that.is_editor)return!0;if(3==ev.which&&!that.is_editor)return $(".selection_right_menu").removeAttr("style"),that.render_file_all_menu(ev,this),!0;$(".file_list_content").bind("mousewheel",(function(){return!1}));var container=$(this),scroll_h=0,con_t=container.offset().top,con_l=container.offset().left,startPos={top:ev.clientY-$(this).offset().top,left:ev.clientX-$(this).offset().left};$(document).unbind("mousemove").mousemove((function(ev){var endPos={top:ev.clientY-con_t>0&&ev.clientY-con_t1?container.height():0,left:ev.clientX-con_l>0&&ev.clientX-con_l1?container.width():0},fixedPoint={top:endPos.top>startPos.top?startPos.top:endPos.top,left:endPos.left>startPos.left?startPos.left:endPos.left};"list"==bt.get_cookie("rank")&&(fixedPoint.top=fixedPoint.top+40);var w=Math.min(Math.abs(endPos.left-startPos.left),con_l+container.width()-fixedPoint.left),h=Math.min(Math.abs(endPos.top-startPos.top),con_t+container.height()-fixedPoint.top);if(ev.clientY-con_t<0){var beyond_t=Math.abs(ev.clientY-con_t);container.scrollTop(container.scrollTop()-beyond_t),0!=container.scrollTop()&&(scroll_h+=beyond_t),h+=scroll_h}if(ev.clientY-(con_t+container.height())>1){var beyond_b=ev.clientY-(con_t+container.height());container.scrollTop(container.scrollTop()+beyond_b),container[0].scrollHeight-container[0].scrollTop!==container[0].clientHeight&&(scroll_h+=beyond_b),h+=scroll_h,fixedPoint.top=fixedPoint.top-scroll_h}if(startPos.top==endPos.top||startPos.left==endPos.left)return!0;that.enter_files_box().show().css({left:fixedPoint.left+"px",top:fixedPoint.top+"px",width:w+"px",height:h+"px"});var box_offset_top=that.enter_files_box().offset().top,box_offset_left=that.enter_files_box().offset().left,box_offset_w=that.enter_files_box().offset().left+that.enter_files_box().width(),box_offset_h=that.enter_files_box().offset().top+that.enter_files_box().height();$(container).find(".file_tr").each((function(i,item){var offset_top=$(item).offset().top,offset_left=$(item).offset().left,offset_h=$(item).offset().top+$(item).height(),offset_w=$(item).offset().left+$(item).width();"icon"==bt.get_cookie("rank")?offset_w>=box_offset_left&&offset_left<=box_offset_w&&offset_h>=box_offset_top&&offset_top<=box_offset_h?$(item).addClass("active"):$(item).removeClass("active"):offset_w>=box_offset_left&&offset_h>=box_offset_top&&offset_top<=box_offset_h?$(item).addClass("active"):$(item).removeClass("active")}))})),$(document).on("mouseup",(function(){var _move_array=[],box_offset_top=that.enter_files_box().offset().top,box_offset_left=that.enter_files_box().offset().left,box_offset_w=that.enter_files_box().offset().left+that.enter_files_box().width(),box_offset_h=that.enter_files_box().offset().top+that.enter_files_box().height();$(container).find(".file_tr").each((function(i,item){var offset_top=$(item).offset().top,offset_left=$(item).offset().left,offset_h=$(item).offset().top+$(item).height(),offset_w=$(item).offset().left+$(item).width();"icon"==bt.get_cookie("rank")?offset_w>=box_offset_left&&offset_left<=box_offset_w&&offset_h>=box_offset_top&&offset_top<=box_offset_h&&_move_array.push($(item).data("index")):offset_w>=box_offset_left&&offset_h>=box_offset_top&&offset_top<=box_offset_h&&_move_array.push($(item).data("index"))})),that.render_file_selected(_move_array),that.enter_files_box().remove(),$(".file_list_content").unbind("mousewheel")})),ev.stopPropagation(),ev.preventDefault()})),$(".file_list_content").on("blur",".set_file_ps",(function(ev){var tr_index=$(this).parents(".file_tr").data("index"),item=that.file_list[tr_index],nval=$(this).val(),oval=$(this).data("value"),_this=this;if(nval==oval)return!1;bt_tools.send("files/set_file_ps",{filename:item.path,ps_type:0,ps_body:nval},(function(rdata){$(_this).data("value",nval)}),{tips:"Set ps",tips:!0})})),$(".file_list_content").on("keyup",".set_file_ps",(function(ev){13==ev.keyCode&&$(this).blur(),ev.stopPropagation()})),$(".file_list_header").on("mousedown",".file_width_resize",(function(ev){return!1;var th,Minus_v,_header,maxlen})),$(".cut_view_model").on("click",(function(){var type=$(this).data("type");$(".file_table_view").addClass("icon"==type?"icon_view":"list_view").removeClass("icon"!=type?"icon_view":"list_view").scrollLeft(0),bt.set_cookie("rank",type),$(this).addClass("active").siblings().removeClass("active")})),$(".file_list_content").on("click",".set_operation_group a",(function(ev){var data=$(this).parents(".file_tr").data(),type=$(this).data("type"),item=that.file_list[data.index];if("more"==type)return!0;item.open=type,item.index=data.index,item.type_tips="file"==item.type?"File":"Directory",that.file_groud_event(item)}))},enter_files_box:function(){return 0==$("#web_mouseDrag").length&&$("
        ",{id:"web_mouseDrag",style:["position:absolute; top:0; left:0;","border:1px solid #072246; background-color: #cce8ff;","filter:Alpha(Opacity=15); opacity:0.15;","overflow:hidden;display:none;z-index:9;"].join("")}).appendTo(".file_table_view"),$("#web_mouseDrag")},clear_table_active:function(){this.file_table_arry=[],$(".file_list_header .file_check").removeClass("active active_2"),$(".file_list_content .file_tr").removeClass("active app_menu_operation"),$(".file_list_content .file_tr .file_ps .foo_menu").removeClass("foo_menu_click"),$(".app_menu_group").remove()},calculate_table_active:function(){var that=this,header_check=$(".file_list_header .file_check");0==this.file_table_arry.length?header_check.removeClass("active active_2").data("checkbox",0):this.file_table_arry.length==this.file_list.length?header_check.addClass("active").removeClass("active_2").data("checkbox",1):header_check.addClass("active_2").removeClass("active").data("checkbox",2),this.file_table_arry.length>0?($(document).unbind("keydown").on("keydown",(function(e){var keyCode=e.keyCode,tagName=e.target.localName.toLowerCase(),is_mac=window.navigator.userAgent.indexOf("Mac")>-1;if("input"==tagName||"textarea"==tagName)return!0;e.ctrlKey&&67==keyCode&&(1==that.file_table_arry.length?(that.file_groud_event($.extend(that.file_table_arry[0],{open:"copy"})),$(".file_all_paste").removeClass("hide")):that.file_table_arry.length>1&&that.batch_file_manage("copy")),e.ctrlKey&&88==keyCode&&(1==that.file_table_arry.length?(that.file_groud_event($.extend(that.file_table_arry[0],{open:"shear"})),$(".file_all_paste").removeClass("hide")):that.file_table_arry.length>1&&that.batch_file_manage("shear"))})),this.file_table_arry.length>1?($(".nav_group.multi").removeClass("hide"),$(".file_menu_tips").addClass("hide")):($(".nav_group.multi").addClass("hide"),$(".file_menu_tips").removeClass("hide"))):($(".nav_group.multi").addClass("hide"),$(".file_menu_tips.multi").removeClass("hide"),$(document).unbind("keydown")),$(".selection_right_menu,.file_path_input .file_dir_item .nav_down_list").removeAttr("style"),that.set_menu_line_view_resize()},set_dir_view_resize:function(){var file_path_input=$(".file_path_input"),file_dir_view=$(".file_path_input .file_dir_view"),_path_width=file_dir_view.attr("data-width"),file_item_hide=null;if(_path_width?parseInt(_path_width):(_path_width=file_dir_view.width(),file_dir_view.attr("data-width",_path_width)),file_dir_view.width()-_path_width<90){var width=0;$($(".file_path_input .file_dir_view .file_dir_item").toArray().reverse()).each((function(){var item_width=0;$(this).attr("data-width")?item_width=parseInt($(this).attr("data-width")):($(this).attr("data-width",$(this).width()),item_width=$(this).width()),width+=item_width,file_path_input.width()-width<=90?$(this).addClass("hide"):$(this).removeClass("hide")}))}var file_item_hide=file_dir_view.children(".file_dir_item.hide").clone(!0);0==file_dir_view.children(".file_dir_item.hide").length?file_path_input.removeClass("active").find(".file_dir_omit").addClass("hide"):(file_item_hide.each((function(){0==$(this).find(".glyphicon-hdd").length&&$(this).find(".file_dir").before('')})),file_path_input.addClass("active").find(".file_dir_omit").removeClass("hide"),file_path_input.find(".file_dir_omit .nav_down_list").empty().append(file_item_hide),file_path_input.find(".file_dir_omit .nav_down_list .file_dir_item").removeClass("hide"))},set_menu_line_view_resize:function(){var menu_width=$(".file_nav_view").width(),disk_list_width=0,batch_list_width=0,_width=0,disk_list=$(".mount_disk_list"),batch_list=$(".nav_group.multi");disk_list.attr("data-width")||disk_list.attr("data-width",disk_list.innerWidth()),batch_list.attr("data-width")||0==batch_list.innerWidth()||-1==batch_list.innerWidth()||batch_list.attr("data-width",batch_list.innerWidth()),disk_list_width=parseInt(disk_list.attr("data-width")),batch_list_width=parseInt(batch_list.attr("data-width")),$(".file_nav_view>.nav_group").not(".mount_disk_list").each((function(){_width+=$(this).innerWidth()})),menu_width-(_width+=$(".menu-header-foot").innerWidth()).nav_btn_group").each((function(index){index>=$(".batch_group_list>.nav_btn_group").length-(indexs+2)?$(this).hide():$(this).show()})),$(".batch_group_list>.nav_btn_group:last-child").removeClass("hide").show()):($(".batch_group_list>.nav_btn_group").css("display","inline-block"),$(".batch_group_list>.nav_btn_group:last-child").addClass("hide"))},set_file_forward:function(){var that=this,forward_path=$(".forward_path span");1==this.file_operating.length?forward_path.addClass("active"):this.file_pointer==this.file_operating.length-1?(forward_path.eq(0).removeClass("active"),forward_path.eq(1).addClass("active")):0==this.file_pointer?(forward_path.eq(0).addClass("active"),forward_path.eq(1).removeClass("active")):forward_path.removeClass("active")},set_file_view:function(){var file_list_content=$(".file_list_content"),height=this.area[1]-$(".file_table_view")[0].offsetTop-170;$(".file_bodys").height(this.area[1]-100),50*this.file_list.length>height?(file_list_content.attr("data-height",file_list_content.data("height")||file_list_content.height()).css({overflow:"hidden","overflow-y":"auto",height:height+"px"}),$(".file_shadow_bottom").css("opacity",1)):(file_list_content.css({overflow:"hidden","overflow-y":"auto",height:height+"px"}),$(".file_shadow_top,.file_shadow_bottom").css("opacity",0))},open_share_view:function(){var that=this;layer.open({type:1,shift:5,closeBtn:2,area:["850px","580px"],title:"Share list",content:'
        Share nameShare addressExpiration dateOpt
        ',success:function(){that.render_share_list(),$(".download_url_list").on("click",".info_down",(function(){var indexs=$(this).attr("data-index");that.file_share_view(that.file_share_list[indexs],"list")})),$(".download_table .download_url_page").on("click","a",(function(e){var _href=$(this).attr("href").match(/p=([0-9]+)$/)[1];that.render_share_list({p:_href}),e.stopPropagation(),e.preventDefault()}))}})},render_share_list:function(param){var that=this,_list="";void 0===param&&(param={p:1}),bt_tools.send("files/get_download_url_list",param,(function(res){that.file_share_list=res.data,res.data.length>0?$.each(res.data,(function(index,item){_list+=''+item.ps+''+item.filename+""+bt.format_data(item.expire)+'Details | Close'})):_list='No share data',$(".download_url_list").html(_list),$(".download_url_page").html(res.page),$(".download_table").on("click",".del_down",(function(){var id=$(this).attr("data-id"),_ps=$(this).attr("data-ps");that.remove_download_url({id:id,fileName:_ps},(function(res){res.status&&that.render_share_list(param),layer.msg(res.msg,{icon:res.status?1:2})}))}))}),"Share list")},remove_check_file:function(arry,key,value){for(var len=arry.length;len--;)arry[len][key]==value&&arry.splice(len,1)},open_download_view:function(){var that=this;that.reader_form_line({url:"DownloadFile",beforeSend:function(data){return{url:data.url,path:data.path,filename:data.filename}},overall:{width:"310px"},data:[{label:"URL address:",name:"url",placeholder:"URL address",value:"http://",eventType:["input","focus"],input:function(){var value,url_list=$(this).val().split("/");$('[name="filename"]').val(url_list[url_list.length-1])}},{label:"Download to:",name:"path",placeholder:"Download to",value:that.file_path},{label:"File name:",name:"filename",placeholder:"Save file name",value:"",eventType:"enter",enter:function(){$(".download_file_view .layui-layer-btn0").click()}}]},(function(form,html){var loadT=bt.open({type:1,title:"Download file",area:"500px",shadeClose:!1,skin:"download_file_view",content:html[0].outerHTML,btn:["Comfirm","Close"],success:function(){form.setEvent()},yes:function(indexo,layero){var ress=form.getVal();if(!bt.check_url(ress.url))return layer.msg("Please enter valid URL address..",{icon:2}),!1;form.submitForm((function(res){that.render_present_task_list(),layer.msg(res.msg,{icon:res.status?1:2}),loadT.close()}))}})}))},set_style_width:function(type,width){var _content=bt.get_cookie("formHeader")||$("#file_list_info").html(),_html="",_reg=new RegExp("\\.file_"+type+"\\s?\\{width\\s?\\:\\s?(\\w+)\\s\\!important;\\}","g"),_defined_config={name:150,type:80,size:80,mtime:150,accept:80,user:80,ps:150};_html=_content.replace(_reg,(function(match,$1,$2,$3){return".file_"+type+"{width:"+(width<80?_defined_config[type]+"px":width+"px")+" !important;}"})),$("#file_list_info").html(_html)},set_file_table_width:function(){var that=this,file_header_width=$(".file_table_view")[0].offsetWidth,auto_num=0,width=0,auto_all_width=0,css="",_width=0,tr_heigth=45,other="",config={};$.each(this.file_header,(function(key,item){"auto"==item?(auto_num++,config[key]=0):(width+=item,css+="."+key+"{width:"+("file_operation"!=key?item:item-16)+"px !important;}")})),this.is_mobile&&$(".file_operation.file_th").attr("style","margin-right:-10px !important;"),45*this.file_list.length>$(".file_list_content").height()?(config.file_tr=file_header_width-(this.is_mobile?0:this.scroll_width),file_header_width=file_header_width,other+=".file_td.file_operation{width:"+(this.file_header.file_operation-(this.is_mobile?0:this.scroll_width)-10)+"px !important;}",other+=".file_th.file_operation{padding-right:"+(10+(this.is_mobile?0:this.scroll_width))+"px !important}"):(file_header_width=file_header_width,config.file_tr=file_header_width,this.is_mobile&&(other+=".file_td.file_operation{width:"+(this.file_header.file_operation-20)+"px !important;}")),config.file_list_header=file_header_width,_width=(auto_all_width=file_header_width-width)/auto_num,$.each(config,(function(key,item){css+="."+key+"{width:"+(0==item?_width:item)+"px !important;}"})),$("#file_list_info").html(css+other)},render_path_list:function(callback){var that=this,html='
        ',path_before="",dir_list=this.file_path.split("/").splice(1),first_dir=this.file_path.split("/")[0];"Windows"===bt.os?(0==dir_list.length&&(dir_list=[]),dir_list.unshift('Local disk ('+first_dir+")")):("/"==this.file_path&&(dir_list=[]),dir_list.unshift("Root dir"));for(var i=0;i '+dir_list[i]+' ';$(".path_input").val("").attr("data-path",this.file_path);var file_dir_view=$(".file_path_input .file_dir_view");file_dir_view.html(html),file_dir_view.attr("data-width",file_dir_view.width()),this.set_dir_view_resize.delay(this,100)},render_path_down_list:function(el,path,callback){var that=this,_html="",next_path=$(el).parent().next().find(".file_dir").attr("title");this.get_dir_list({path:path},(function(res){$.each(that.data_reconstruction(res.DIR),(function(index,item){var _path=("/"!=path?path:"")+"/"+item.filename;_html+='
      • '+item.filename+"
      • "})),$(el).html(_html)}))},reader_file_list:function(data,callback){var that=this,select_page_num="",next_path="",model=bt.get_cookie("rank"),isPaste=bt.get_cookie("record_paste_type");"null"!=isPaste&&null!=isPaste?$(".file_nav_view .file_all_paste").removeClass("hide"):$(".file_nav_view .file_all_paste").addClass("hide"),$(".file_table_view").removeClass(".list_view,.icon_view").addClass("list"==model?"list_view":"icon_view"),$(".cut_view_model:nth-child("+("list"==model?"2":"1")+")").addClass("active").siblings().removeClass("active"),this.file_images_list=[],this.get_dir_list(data,(function(res){if(!1===res.status&&res.msg.indexOf("The specified directory does not exist!")>-1)return that.reader_file_list({path:"/www"});that.file_path=that.path_check(res.PATH),that.file_list=$.merge(that.data_reconstruction(res.DIR,"DIR"),that.data_reconstruction(res.FILES)),that.is_recycle=res.FILE_RECYCLE,that.file_store_list=res.STORE,bt.set_cookie("Path",that.path_check(res.PATH)),that.reader_file_list_content(that.file_list,(function(rdata){$(".path_input").attr("data-path",that.file_path),$(".file_nav_view .multi").addClass("hide"),$(".selection_right_menu").removeAttr("style"),$.each(["100","200","500","1000","2000"],(function(index,item){select_page_num+='"}));var page=$(res.PAGE);page.append('per pageitem(s)"),$(".filePage").html('
        Total '+rdata.is_dir_num+" directory, "+(that.file_list.length-rdata.is_dir_num)+'file(s), size:Click to calculate
        '+page[0].outerHTML),data.is_operating&&that.file_operating[that.file_pointer]!=res.PATH&&(void 0!==(next_path=that.file_operating[that.file_pointer+1])&&next_path!=res.PATH&&that.file_operating.splice(that.file_pointer+1),that.file_operating.push(res.PATH),that.file_pointer=that.file_operating.length-1),that.render_path_list(),that.set_file_forward(),that.render_favorites_list(),that.set_file_view(),that.set_file_table_width(),callback&&callback(res)}))}))},data_reconstruction:function(data,type,callback){var that=this,arry=[],info_ps=[["/etc","PS: System files directory"],["/home","PS: Home directory"],["/tmp","PS: Common temporary files directory"],["/root","PS: Main directory of system admin"],["/usr","PS: System application directory"],["/boot","PS: System run directory"],["/lib","PS: System source file directory"],["/mnt","PS: Store temporary mapped file system"],["/www","PS: Aapanel program directory"],["/bin","PS: Store binary executable file directory"],["/dev","PS: Storage device file directory"],["/www/wwwlogs","PS: Default site logs directory"],["/www/server","PS: Aapanel soft installed directory"],["/www/wwwlogs","PS: Site logs directory"],["/www/Recycle_bin",lan.files.recycle_bin_dir],["/www/server/panel","PS: Aapanel main program directory, do not move"],["/www/server/panel/plugin","PS: Aapanel plugin directory"],["/www/server/panel/BTPanel","PS: Aapanel directory"],["/www/server/panel/BTPanel/static","PS: Aapanel static directory"],["/www/server/panel/BTPanel/templates","PS: Aapanel templates directory"],[bt.get_cookie("backup_path"),"PS: Default backup directory"],[bt.get_cookie("sites_path"),"PS: Default site directory"]];return data.length<1?[]:($.each(data,(function(index,item){var itemD=item.split(";"),fileMsg="",fileN=itemD[0].split("."),extName=fileN[fileN.length-1];switch(itemD[0]){case".user.ini":fileMsg=lan.files.php_profile;break;case".htaccess":fileMsg=lan.files.apache_profile;break;case"swap":fileMsg=lan.files.swap_file}-1!=itemD[0].indexOf("Recycle_bin")&&(fileMsg=lan.files.swap_file),-1!=itemD[0].indexOf(".upload.tmp")&&(fileMsg=lan.files.recycle_bin_dir);for(var i=0;i0?($.each(this.file_store_list,(function(index,item){html+='
      • '+item.name+"
      • "})),html+='
      • Management
      • '):html='
      • (Empty)
      • ',$(".favorites_file_path .nav_down_list").html(html)},set_favorites_manage:function(){var that=this;layer.open({type:1,title:"Manage Favorites",area:["850px","580px"],closeBtn:2,shift:5,shadeClose:!1,content:"
        PathOpt
        ",success:function(layers){that.render_favorites_type_list(),setTimeout((function(){$(layers).css("top",($(window).height()-$(layers).height())/2)}),50)},cancel:function(){that.reader_file_list({path:that.file_path})}})},render_favorites_type_list:function(){var _detail="";this.$http("get_files_store",(function(rdata){rdata.length>0?$.each(rdata,(function(ind,item){_detail+=''+item.path+'Del"})):_detail='No favorites',$(".favorites_body").html(_detail),jQuery.prototype.fixedThead?$(".stroe_tab_list .divtable").fixedThead({resize:!1}):$(".stroe_tab_list .divtable").css({overflow:"auto"})}))},load_favorites_index_list:function(){var that=this;this.$http("get_files_store",(function(rdata){that.file_store_list=rdata,that.render_favorites_list()}))},del_favorites:function(path){var that=this;layer.confirm("Comfirm delete path【"+path+"】?",{title:"Delete favorites",closeBtn:2,icon:3},(function(index){that.$http("del_files_store",{path:path},(function(res){res.status&&that.render_favorites_type_list(),layer.msg(res.msg,{icon:res.status?1:2})}))}))},reader_file_list_content:function(data,callback){var _html="",that=this,is_dir_num=0,images_num=0;$.each(data,(function(index,item){var _title=item.filename,only_id=bt.get_random(10),path=(that.file_path+"/"+item.filename).replace("//","/"),is_compress=that.determine_file_type(item.ext,"compress"),is_editor_tips=function(){var _openTitle="open";switch(that.determine_file_type(item.ext)){case"images":_openTitle="Preview";break;case"video":_openTitle="Play";break;default:_openTitle="compress"==that.determine_file_type(item.ext)?"":"Edit"}return"dir"==item.type&&(_openTitle="Open"),_openTitle}(item);that.file_list[index].only_id=only_id,_html+='
        "+item.filename+item.soft_link+""+(item.caret?'':"")+(0!=item.down_id?'':"")+'
        '+("dir"==item.type?"directory":that.ext_type_tips(item.ext))+'
        '+item.user+" / "+item.root_level+'
        '+("dir"==item.type?'Calculate':bt.format_size(item.size))+'
        '+bt.format_data(item.mtime)+'
        '+(item.is_os_ps?item.ps:'')+'
        ',"dir"==item.type&&is_dir_num++,item.path=path,item.open_type=that.determine_file_type(item.ext),"images"==item.open_type&&(item.images_id=images_num,that.file_images_list.push(item.path),images_num++)})),$(".file_list_content").html(_html),callback&&callback({is_dir_num:is_dir_num}),that.clear_table_active()},render_file_disk_list:function(){var that=this,html="",_li="";that.get_disk_list((function(res){$.each(res,(function(index,item){html+='",_li+='
      • '+("/"==item.path?"Root dir":item.path)+" ("+item.size[2]+")
      • "})),$(".mount_disk_list").html('
        '+lan.files.mounted_disk+'
        '+html+"
        "),that.set_menu_line_view_resize()}))},render_file_groud_menu:function(ev,el){var that=this,index=$(el).data("index"),_openTitle="Open",data=this.file_list[index],compression=["zip","rar","gz","war","tgz","bz2"],offsetNum=0,config={open:_openTitle,split_0:!0,download:"Download",share:"Share file",cancel_share:"Cancel share",favorites:"Favorites file",cancel_favorites:"Cancel favorites",split_1:!0,authority:"Permission",split_2:!0,copy:"Copy",shear:"Cut",rename:"Rename",del:"Delete",split_3:!0,compress:"Compress",unzip:"Unzip",open_find_dir:"Open file location",split_4:!0,property:"Properties"};switch(this.determine_file_type(data.ext)){case"images":_openTitle="Preview";break;case"video":_openTitle="Play";break;default:_openTitle="Edit"}config.open="dir"==data.type?"Open":_openTitle,"dir"===data.type&&delete config.download,"compress"==data.open_type&&delete config.open,0!=data.down_id?delete config.share:(delete config.cancel_share,config.share="dir"==data.type?"Share dir":"Share file"),!1!==data.caret?delete config.favorites:(delete config.cancel_favorites,config.favorites="dir"==data.type?"Favorites dir":"Favorites file"),"php"==data.ext&&(config.dir_kill="文件查杀"),"php"!=data.ext&&"dir"!=data.type&&delete config.dir_kill;var num=0;$.each(compression,(function(index,item){item==data.ext&&num++})),0==num&&delete config.unzip,data.is_search?config.open_find_dir="dir"==data.type?"Open dir":"Open file location":delete config.open_find_dir,this.file_selection_operating=config,this.reader_menu_list({el:$(".selection_right_menu"),ev:ev,data:data,list:config})},render_file_all_menu:function(ev,el){var that=this,config={refresh:"Refresh",split_0:!0,upload:"Upload",create:["New file/folder",{create_dir:"New folder",create_files:"New file",soft_link:"Softlink"}],web_shell:"Terminal",split_1:!0,paste:"Paste"},offsetNum=0,isPaste=bt.get_cookie("record_paste_type");"null"!=isPaste&&null!=isPaste||(delete config.split_1,delete config.paste),this.reader_menu_list({el:$(".selection_right_menu"),ev:ev,data:{},list:config})},render_files_multi_menu:function(ev){var that=this,config_group=[["copy","Copy"],["shear","cut"],["authority","Permission"],["compress","Compress"],["del","Delete"]],el=$(".selection_right_menu").find("ul"),el_height=el.height(),el_width=el.width(),left=ev.clientX-(this.area[0]-ev.clientX'):el.append($('
      • '+mitem[1]+"
      • ").append(null).on("click",{type:mitem[0],data:that.file_table_arry},(function(ev){$(".selection_right_menu").removeAttr("style"),that.batch_file_manage(ev.data.type),ev.stopPropagation(),ev.preventDefault()})))})),$(".selection_right_menu").css({left:left,top:ev.clientY-(this.area[1]-ev.clientY'):(Array.isArray(item)&&($children=$('
          '),$children_list=$children.find(".set_group"),$.each(item[1],(function(keys,items){$children_list.append($('
        • '+items+"
        • ").on("click",{type:keys,data:config.data},(function(ev){that.file_groud_event($.extend(ev.data.data,{open:ev.data.type,index:parseInt($(config.ev.currentTarget).data("index")),element:config.ev.currentTarget,type_tips:"dir"==config.data.type?"folder":"file"})),config.el.removeAttr("style"),ev.stopPropagation(),ev.preventDefault()})))}))),el.append($('
        • '+(Array.isArray(item)?item[0]:item)+"
        • ").append($children).on("click",{type:key,data:config.data},(function(ev){that.file_groud_event($.extend(ev.data.data,{open:ev.data.type,index:parseInt($(config.ev.currentTarget).data("index")),element:config.ev.currentTarget,type_tips:"dir"==config.data.type?"folder":"file"})),"compress"!=key&&"create"!=key&&config.el.removeAttr("style"),ev.stopPropagation(),ev.preventDefault()}))))})),el_height=el.innerHeight(),top=config.ev.clientY-(this.area[1]-config.ev.clientY'+lan.files.recycle_bin_warning+"",(function(){that.$http("SetBatchData",obj,(function(res){res.status&&that.reader_file_list({path:that.file_path}),layer.msg(res.msg,{icon:res.status?1:2})}))}))},batch_file_paste:function(){var that=this,_pCookie=bt.get_cookie("record_paste_type");this.check_exists_files_req({dfile:this.file_path},(function(result){if(result.length>0){for(var tbody="",i=0;i'+result[i].filename+""+ToSize(result[i].size)+""+getLocalTime(result[i].mtime)+"";var mbody;SafeMessage("The following files will be overwritten",'
          '+tbody+"
          File nameSizeLast edit time
          ",(function(){that.$http("BatchPaste",{type:_pCookie,path:that.file_path},(function(rdata){rdata.status&&(bt.set_cookie("record_paste_type",null),that.reader_file_list({path:that.file_path})),layer.msg(rdata.msg,{icon:rdata.status?1:2})}))}))}else that.$http("BatchPaste",{type:_pCookie,path:that.file_path},(function(rdata){rdata.status&&(bt.set_cookie("record_paste_type",null),that.reader_file_list({path:that.file_path})),layer.msg(rdata.msg,{icon:rdata.status?1:2})}))}))},recycle_bin_view:function(){var that=this;layer.open({type:1,shift:5,closeBtn:2,area:["80%","606px"],title:lan.files.recycle_bin_title,content:'
          '+lan.files.recycle_bin_on+'
          '+lan.files.recycle_bin_on_db+'
          '+lan.files.recycle_bin_ps+'

          '+lan.files.recycle_bin_type1+'

          '+lan.files.recycle_bin_type2+'

          '+lan.files.recycle_bin_type3+'

          '+lan.files.recycle_bin_type4+'

          '+lan.files.recycle_bin_type5+'

          '+lan.files.recycle_bin_type6+'

          '+lan.files.recycle_bin_th1+" "+lan.files.recycle_bin_th2+" "+lan.files.recycle_bin_th3+' '+lan.files.recycle_bin_th4+' '+lan.files.recycle_bin_th5+'
          ',success:function(){-1!=window.location.href.indexOf("database")?($(".re-con-menu p:last-child").addClass("on").siblings().removeClass("on"),that.render_recycle_list(6)):that.render_recycle_list(1),$(".re-con-menu").on("click","p",(function(){var _type=$(this).data("type");$(this).addClass("on").siblings().removeClass("on"),that.render_recycle_list(_type)}))}})},render_recycle_list:function(num){var that=this;this.$http("Get_Recycle_bin",(function(rdata){var body="";switch($("#Set_Recycle_bin").attr("checked",rdata.status),$("#Set_Recycle_bin_db").attr("checked",rdata.status_db),num){case 1:for(var i=0;i20&&(shortwebname=shortwebname.substring(0,20)+"..."),shortpath.length>20&&(shortpath=shortpath.substring(0,20)+"..."),body+=' '+shortwebname+' '+shortpath+" "+ToSize(rdata.dirs[i].size)+" "+getLocalTime(rdata.dirs[i].time)+' "+lan.files.recycle_bin_re+' | "+lan.files.recycle_bin_del+" "}for(var i=0;i20&&(shortwebname=shortwebname.substring(0,20)+"..."),shortpath.length>20&&(shortpath=shortpath.substring(0,20)+"..."),body+=' '+shortwebname+' '+shortpath+" "+ToSize(rdata.files[i].size)+" "+getLocalTime(rdata.files[i].time)+' "+lan.files.recycle_bin_re+' | "+lan.files.recycle_bin_del+" "}else{var shortwebname=rdata.files[i].name.replace(/'/,"\\'"),shortpath=rdata.files[i].dname;shortwebname.length>20&&(shortwebname=shortwebname.substring(0,20)+"..."),shortpath.length>20&&(shortpath=shortpath.substring(0,20)+"..."),body+=' '+shortwebname.replace("BTDB_","")+' mysql://'+shortpath.replace("BTDB_","")+" - "+getLocalTime(rdata.files[i].time)+' "+lan.files.recycle_bin_re+' | "+lan.files.recycle_bin_del+" "}return void $("#RecycleBody").html(body);case 2:for(var i=0;i20&&(shortwebname=shortwebname.substring(0,20)+"..."),shortpath.length>20&&(shortpath=shortpath.substring(0,20)+"..."),body+=' '+shortwebname+' '+shortpath+" "+ToSize(rdata.dirs[i].size)+" "+getLocalTime(rdata.dirs[i].time)+' "+lan.files.recycle_bin_re+' | "+lan.files.recycle_bin_del+" "}return void $("#RecycleBody").html(body);case 3:for(var i=0;i20&&(shortwebname=shortwebname.substring(0,20)+"..."),shortpath.length>20&&(shortpath=shortpath.substring(0,20)+"..."),body+=' '+shortwebname+' '+shortpath+" "+ToSize(rdata.files[i].size)+" "+getLocalTime(rdata.files[i].time)+' "+lan.files.recycle_bin_re+' | "+lan.files.recycle_bin_del+" "}return void $("#RecycleBody").html(body);case 4:for(var i=0;i20&&(shortwebname=shortwebname.substring(0,20)+"..."),shortpath.length>20&&(shortpath=shortpath.substring(0,20)+"..."),body+=' '+shortwebname+' '+shortpath+" "+ToSize(rdata.files[i].size)+" "+getLocalTime(rdata.files[i].time)+' "+lan.files.recycle_bin_re+' | "+lan.files.recycle_bin_del+" "}return void $("#RecycleBody").html(body);case 5:for(var i=0;i20&&(shortwebname=shortwebname.substring(0,20)+"..."),shortpath.length>20&&(shortpath=shortpath.substring(0,20)+"..."),body+=' '+shortwebname+' '+shortpath+" "+ToSize(rdata.files[i].size)+" "+getLocalTime(rdata.files[i].time)+' "+lan.files.recycle_bin_re+' | "+lan.files.recycle_bin_del+" "}return void $("#RecycleBody").html(body);case 6:for(var i=0;i20&&(shortwebname=shortwebname.substring(0,20)+"..."),shortpath.length>20&&(shortpath=shortpath.substring(0,20)+"..."),body+=' '+shortwebname.replace("BTDB_","")+' mysql://'+shortpath.replace("BTDB_","")+" - "+getLocalTime(rdata.files[i].time)+' "+lan.files.recycle_bin_re+' | "+lan.files.recycle_bin_del+" "}return void $("#RecycleBody").html(body)}function getFileName(name){var text=name.split("."),n;return text=text[text.length-1]}function ReisImage(fileName){for(var exts=["jpg","jpeg","png","bmp","gif","tiff","ico"],i=0;i
          '+lan.files.recycle_bin_on+'
          '+lan.files.recycle_bin_on_db+'
          '+lan.files.recycle_bin_ps+'

          '+lan.files.recycle_bin_type1+'

          '+lan.files.recycle_bin_type2+'

          '+lan.files.recycle_bin_type3+'

          '+lan.files.recycle_bin_type4+'

          '+lan.files.recycle_bin_type5+'

          '+lan.files.recycle_bin_type6+'

          ',success:function(){-1!=window.location.href.indexOf("database")?($(".re-con-menu p:last-child").addClass("on").siblings().removeClass("on"),$(".re-con-menu p:eq(5)").click()):$(".re-con-menu p:eq(0)").click();var render_recycle=that.render_recycle_list();$(".re-con-menu").on("click","p",(function(){var _type=$(this).data("type");$(this).addClass("on").siblings().removeClass("on"),render_recycle.$refresh_table_list(!0)}))}})},render_recycle_list:function(){var that=this;$("#recycle_table").empty();var recycle_list=bt_tools.table({el:"#recycle_table",url:"/files?action=Get_Recycle_bin",height:480,dataFilter:function(res){var files=[];switch($(".re-con-menu p.on").index()){case 0:for(let i=0;i-1?(item.dname=item.dname.replace("BTDB_",""),item.name=item.name.replace("BTDB_",""),files.push($.extend(item,{type:"files"}))):"images"==ext?files.push($.extend(item,{type:ext})):files.push($.extend(item,{type:"files"}))}break;case 1:for(let i=0;i-1&&(item.dname=item.dname.replace("BTDB_",""),item.name=item.name.replace("BTDB_",""),console.log(),files.push($.extend(item,{type:"files"})))}}return $("#Set_Recycle_bin").attr("checked",res.status),$("#Set_Recycle_bin_db").attr("checked",res.status_db),{data:files}},column:[{type:"checkbox",class:"",width:18},{fid:"name",title:lan.files.recycle_bin_th1,width:155,template:function(row){return'
          '+row.name+"
          "}},{fid:"dname",title:lan.files.recycle_bin_th2,template:function(row){return''+row.dname+""}},{fid:"size",title:lan.files.recycle_bin_th3,width:85,template:function(row){return''+bt.format_size(row.size)+""}},{fid:"time",title:lan.files.recycle_bin_th4,width:180,template:function(row,index){return''+bt.format_data(row.time)+""}},{type:"group",align:"right",width:250,title:lan.files.recycle_bin_th5,group:[{title:lan.files.recycle_bin_re,event:function(row,index,ev,key,that){bt_file.ReRecycleBin(row.rname,(function(){that.$delete_table_row(index)}))}},{title:lan.files.recycle_bin_del,event:function(row,index,ev,key,that){bt_file.DelRecycleBin(row.rname,(function(){that.$delete_table_row(index)}))}}]}],tootls:[{type:"batch",positon:["left","bottom"],placeholder:"Please Choose",buttonValue:"Execute",disabledSelectValue:"Please select the port that needs batch operation!",selectList:[{title:"Restore",url:"/files?action=Re_Recycle_bin",load:!0,param:function(row){return{path:row.rname}},callback:function(that){bt.confirm({title:"Restore files",msg:"Batch restore selected files, do you want to continue?",icon:0},(function(index){layer.close(index),that.start_batch({},(function(list){for(var html="",i=0;i"+item.name+'
          '+(item.request.status?"Successful recovery":"Recovery failed")+"
          "}recycle_list.$batch_success_table({title:"Restore files",th:"File name",html:html}),recycle_list.$refresh_table_list(!0)}))}))}},{title:"Delete files permanently",url:"/files?action=Del_Recycle_bin",load:!0,param:function(row){return{path:row.rname}},callback:function(that){bt.confirm({title:"Delete selected files",msg:"Delete the selected file. The file will be completely deleted and cannot be recovered. Do you want to continue?",icon:0},(function(index){layer.close(index),that.start_batch({},(function(list){for(var html="",i=0;i"+item.name+'
          '+(item.request.status?"Successfully deleted":"Failed to delete")+"
          "}recycle_list.$batch_success_table({title:"Delete multiple files",th:"File name",html:html}),recycle_list.$refresh_table_list(!0)}))}))}}]}]});return bt_tools.$fixed_table_thead("#recycle_table .divtable"),recycle_list},Set_Recycle_bin:function(db){var loadT=layer.msg(lan.public.the,{icon:16,time:0,shade:[.3,"#000"]}),that=this,data={};1==db&&(data={db:db}),$.post("/files?action=Recycle_bin",data,(function(rdata){layer.close(loadT),rdata.status&&null==db&&(that.is_recycle=$("#Set_Recycle_bin").prop("checked")),layer.msg(rdata.msg,{icon:rdata.status?1:5})}))},ReRecycleBin:function(path,obj){layer.confirm(lan.files.recycle_bin_re_msg,{title:lan.files.recycle_bin_re_title,closeBtn:2,icon:3},(function(){var loadT=layer.msg(lan.files.recycle_bin_re_the,{icon:16,time:0,shade:[.3,"#000"]});$.post("/files?action=Re_Recycle_bin","path="+encodeURIComponent(path),(function(rdata){layer.close(loadT),layer.msg(rdata.msg,{icon:rdata.status?1:5}),$(obj).parents("tr").remove()}))}))},DelRecycleBin:function(path,obj){layer.confirm(lan.files.recycle_bin_del_msg,{title:lan.files.recycle_bin_del_title,closeBtn:2,icon:3},(function(){var loadT=layer.msg(lan.files.recycle_bin_del_the,{icon:16,time:0,shade:[.3,"#000"]});$.post("/files?action=Del_Recycle_bin","path="+encodeURIComponent(path),(function(rdata){layer.close(loadT),layer.msg(rdata.msg,{icon:rdata.status?1:5}),$(obj).parents("tr").remove()}))}))},CloseRecycleBin:function(){var _this=this;layer.confirm(lan.files.recycle_bin_close_msg,{title:lan.files.recycle_bin_close,closeBtn:2,icon:3},(function(){var loadT=layer.msg("
          "+lan.files.recycle_bin_close_the+"
          ",{icon:16,time:0,shade:[.3,"#000"]});setTimeout((function(){getSpeed(".myspeed")}),1e3),$.post("/files?action=Close_Recycle_bin","",(function(rdata){layer.close(loadT),layer.msg(rdata.msg,{icon:rdata.status?1:5}),_this.render_recycle_list()}))}))},open_property_view:function(data){console.log(data);var _this=this;_this.$http("get_file_attribute",{filename:data.path},(function(res){layer.open({type:1,closeBtn:2,title:"[ "+data.filename+" ] - "+(data.is_dir?"Folder":"File")+" properties",area:["580px","500px"],shadeClose:!1,content:'
          General Detail History
          Type:
          '+(res.is_dir||res.is_link?res.st_type:_this.ext_type_tips(res.st_type))+'
          Location:
          '+res.path+'
          Size:
          '+bt.format_size(res.st_size)+" ("+_this.font_thousandth(res.st_size)+' byte)
          Permissions:
          '+res.mode+'
          Group:
          '+res.group+'
          User:
          '+res.user+'
          Visit time:
          '+bt.format_data(res.st_atime)+'
          Modified time:
          '+bt.format_data(res.st_mtime)+'
          Properties
          Value
          Modified time
          Size
          MD5
          OPT
          ',success:function(layero,index){$(".bt-property-setting .tab-nav span").click((function(){var index=$(this).index();$(this).addClass("on").siblings().removeClass("on"),$(".property-box:eq("+index+")").addClass("active").siblings().removeClass("active")})),$(".history_box_view").on("click",".open_history_file",(function(){var _history=$(this).attr("data-time");openEditorView(0,data.path),setTimeout((function(){aceEditor.openHistoryEditorView({filename:data.path,history:_history},(function(){layer.close(index),$(".ace_conter_tips").show(),$(".ace_conter_tips .tips").html("Read-only file, the file is "+_item.path+", historic version [ "+bt.format_data(new Number(_history))+' ]Click to restore')}))}),500)})),$(".history_box_view").on("click",".recovery_file_historys",(function(){aceEditor.event_ecovery_file(this)}));var config={filename:["Name",data.filename],type:["Type",res.is_dir||res.is_link?res.st_type:_this.ext_type_tips(res.st_type)],path:"Location",st_size:["Size",bt.format_size(res.st_size)+" ("+_this.font_thousandth(res.st_size)+" byte)"],st_atime:["Visit time",bt.format_data(res.st_atime)],st_mtime:["Modified time",bt.format_data(res.st_mtime)],st_ctime:["Metadata modification time",bt.format_data(res.st_ctime)],md5:"MD5",sha1:"sha1",user:"User",group:"Group",mode:"Permissions",st_uid:"UID",st_gid:"GID",st_nlink:"Num of inode links",st_ino:"inode node num",st_mode:"inode protection mode",st_dev:"inode resident device"},html="",html2="";for(var key in config)if(Object.hasOwnProperty.call(config,key)){var element=config[key],value=$.isArray(element)?element[1]:res[key];html+='
          '+($.isArray(element)?element[0]:element)+'
          '+value+"
          "}for(let i=0;i
          '+bt.format_data(item.st_mtime)+'
          '+bt.format_size(item.st_size)+"
          "+item.md5+'
          view  |  restore
          '}""===html2&&(html2+='
          No historical version
          '),$(".details_list").html(html),$(".history_list ").html(html2),_this.fixed_table_thead(".details_box_view"),_this.fixed_table_thead(".history_box_view ")}})}))},fixed_table_thead:function(el){$(el).scroll((function(){var scrollTop=this.scrollTop;this.querySelector("thead").style.transform="translateY("+scrollTop+"px)"}))},font_thousandth:function(num){var source=String(num).split(".");return source[0]=source[0].replace(new RegExp("(\\d)(?=(\\d{3})+$)","ig"),"$1,"),source.join(".")},open_images_preview:function(data){var that=this,mask=$('
          '+data.filename+'
          '),images_config={natural_width:0,natural_height:0,init_width:0,init_height:0,preview_width:0,preview_height:0,current_width:0,current_height:0,current_left:0,current_top:0,rotate:0,scale:1,images_mouse:!1};if($(".preview_images_mask").length>0)return $("#preview_images").attr("src","/download?filename="+data.path),!1;function auto_images_size(transition){var rotate=Math.abs(images_config.rotate/90),preview_width=rotate%2==0?images_config.preview_width:images_config.preview_height,preview_height=rotate%2==0?images_config.preview_height:images_config.preview_width,preview_images=$("#preview_images"),css_config={};images_config.init_width=images_config.natural_width,images_config.init_height=images_config.natural_height,images_config.init_width>preview_width&&(images_config.init_width=preview_width,images_config.init_height=parseFloat((preview_width/images_config.natural_width*images_config.init_height).toFixed(2))),images_config.init_height>preview_height&&(images_config.init_width=parseFloat((preview_height/images_config.natural_height*images_config.init_width).toFixed(2)),images_config.init_height=preview_height),images_config.current_width=parseFloat(images_config.init_width*images_config.scale),images_config.current_height=parseFloat(images_config.init_height*images_config.scale),images_config.current_left=parseFloat(((images_config.preview_width-images_config.current_width)/2).toFixed(2)),images_config.current_top=parseFloat(((images_config.preview_height-images_config.current_height)/2).toFixed(2)),css_config={width:images_config.current_width,height:images_config.current_height,top:images_config.current_top,left:images_config.current_left,display:"inline",transform:"rotate("+images_config.rotate+"deg)",opacity:1,transition:"all 100ms"},!1===transition&&delete css_config.transition,preview_images.css(css_config)}$("body").css("overflow","hidden").append(mask),images_config.preview_width=mask[0].clientWidth,images_config.preview_height=mask[0].clientHeight,$(".preview_body img").load((function(){var img=$(this)[0];$(this).attr("data-index")||$(this).attr("data-index",data.images_id),images_config.natural_width=img.naturalWidth,images_config.natural_height=img.naturalHeight,auto_images_size(!1)})),$(".preview_images_mask .preview_head").on("mousedown",(function(e){e=e||window.event;var drag=$(this).parent();if($("body").addClass("select"),$(this).onselectstart=$(this).ondrag=function(){return!1},!$(e.target).hasClass("preview_close")){var diffX=e.clientX-drag.offset().left,diffY=e.clientY-drag.offset().top;$(document).on("mousemove",(function(e){var left=(e=e||window.event).clientX-diffX,top=e.clientY-diffY;left<0?left=0:left>window.innerWidth-drag.width()&&(left=window.innerWidth-drag.width()),top<0?top=0:top>window.innerHeight-drag.height()&&(top=window.innerHeight-drag.height()),drag.css({left:left,top:top,margin:0})})).on("mouseup",(function(){$(this).unbind("mousemove mouseup")}))}})),$(".preview_images_mask #preview_images").on("mousedown",(function(e){e=e||window.event,$(this).onselectstart=$(this).ondrag=function(){return!1};var images=$(this),preview=$(".preview_images_mask").offset(),diffX=e.clientX-preview.left,diffY=e.clientY-preview.top;$(".preview_images_mask").on("mousemove",(function(e){var offsetX=(e=e||window.event).clientX-preview.left-diffX,offsetY=e.clientY-preview.top-diffY,rotate=Math.abs(images_config.rotate/90),preview_width=rotate%2==0?images_config.preview_width:images_config.preview_height,preview_height=rotate%2==0?images_config.preview_height:images_config.preview_width,left,top;if(images_config.current_width>preview_width){var max_left=preview_width-images_config.current_width;(left=images_config.current_left+offsetX)>0?left=0:leftpreview_height){var max_top=preview_height-images_config.current_height;(top=images_config.current_top+offsetY)>0?top=0:toppreview_height&&images_config.current_top<=0&&images_config.current_height-preview_height<=images_config.current_top&&(images_config.current_top-=offsetY),images.css({left:images_config.current_left,top:images_config.current_top})})).on("mouseup",(function(){$(this).unbind("mousemove mouseup")})).on("dragstart",(function(){e.preventDefault()}))})).on("dragstart",(function(){return!1})),$(".preview_close").click((function(e){$(".preview_images_mask").remove()})),$(".preview_toolbar a").click((function(){var index=$(this).index(),images=$("#preview_images");switch(index){case 0:case 1:images_config.rotate=index?images_config.rotate+90:images_config.rotate-90,auto_images_size();break;case 2:case 3:if(3==images_config.scale&&2==index||.2==images_config.scale&&3==index)return layer.msg(images_config.scale>=1?"The image is the maximum size":"The image is the minimum size"),!1;images_config.scale=(2==index?Math.round(10*(images_config.scale+.4)):Math.round(10*(images_config.scale-.4)))/10,auto_images_size();break;case 4:var scale_offset=images_config.rotate%360;scale_offset>=180?images_config.rotate+=360-scale_offset:images_config.rotate-=scale_offset,images_config.scale=1,auto_images_size()}})),$(".preview_full,.preview_small").click((function(){$(this).hasClass("preview_full")?($(this).addClass("hidden").prev().removeClass("hidden"),images_config.preview_width=that.area[0],images_config.preview_height=that.area[1],mask.css({width:that.area[0],height:that.area[1],top:0,left:0,margin:0}).data("type","full"),auto_images_size()):($(this).addClass("hidden").next().removeClass("hidden"),$(".preview_images_mask").removeAttr("style"),images_config.preview_width=750,images_config.preview_height=650,auto_images_size())})),$(".preview_cut_view a").click((function(){var images_src="",preview_images=$("#preview_images"),images_id=parseInt(preview_images.attr("data-index"));$(this).index()?(images_id=images_id==that.file_images_list.length-1?0:images_id+1,images_src=that.file_images_list[images_id]):(images_id=0===images_id?that.file_images_list.length-1:images_id-1,images_src=that.file_images_list[images_id]),preview_images.attr("data-index",images_id).attr("src","/download?filename="+images_src),$(".preview_title").html(that.get_path_filename(images_src))}))},open_video_play:function(data){var old_filename=data.path,imgUrl="/download?filename="+data.path,p_tmp=data.path.split("/"),path=p_tmp.slice(0,p_tmp.length-1).join("/");layer.open({type:1,closeBtn:2,title:'Playing ['+p_tmp[p_tmp.length-1]+"]",area:["890px","402px"],shadeClose:!1,skin:"movie_pay",content:'
          ',success:function(){$.post("/files?action=get_videos",{path:path},(function(rdata){for(var video_list='',index=0,i=0;i"}video_list+="
          File nameSize
          '+rdata[i].name+''+ToSize(rdata[i].size)+"
          ",$(".video-list").html(video_list).scrollTop(34*index)}))}})},play_file:function(obj,filename){if($("#btvideo video").attr("data-filename")==filename)return!1;var imgUrl,v='';$("#btvideo").html(v);var p_tmp=filename.split("/");$(".btvideo-title").html(p_tmp[p_tmp.length-1]),$(".video-avt").removeClass("video-avt"),$(obj).parents("tr").addClass("video-avt")},copy_file_or_dir:function(data){bt.set_cookie("record_paste",data.path),bt.set_cookie("record_paste_type","copy"),$(".file_all_paste").removeClass("hide"),layer.msg("Copy successfully. Please click [Paste] or Ctrl + V to paste")},cut_file_or_dir:function(data){bt.set_cookie("record_paste",data.path),bt.set_cookie("record_paste_type","cut"),$(".file_all_paste").removeClass("hide"),layer.msg("Cut successfully. Please click [Paste] or Ctrl + V to paste")},paste_file_or_dir:function(){var that=this,_isPaste=bt.get_cookie("record_paste_type"),_paste=bt.get_cookie("record_paste"),_filename="";if("null"!=_paste&&null!=_paste&&(_filename=_paste.split("/").pop()),that.file_path.indexOf(_paste)>-1)return layer.msg("Can not paste ["+_filename+"] here, Because the item cannot be pasted into itself.",{icon:0}),!1;if("null"!=_isPaste&&null!=_isPaste)switch(_isPaste){case"cut":case"copy":this.check_exists_files_req({dfile:this.file_path,filename:_filename},(function(result){if(result.length>0){for(var tbody="",i=0;i'+result[i].filename+""+ToSize(result[i].size)+""+getLocalTime(result[i].mtime)+"";var mbody;SafeMessage("This files will be overwritten",'
          '+tbody+"
          File nameSizeLast edit time
          ",(function(){that.config_paste_to(_paste,_filename)}))}else that.config_paste_to(_paste,_filename)}));break;case"1":case"2":that.batch_file_paste()}},config_paste_to:function(path,_filename){var that=this,_type=bt.get_cookie("record_paste_type");this.$http("copy"==_type?"CopyFile":"MvFile",{sfile:path,dfile:this.file_path+"/"+_filename},(function(rdata){rdata.status&&(bt.set_cookie("record_paste",null),bt.set_cookie("record_paste_type",null),that.reader_file_list({path:that.file_path})),layer.msg(rdata.msg,{icon:rdata.status?1:2})}))},rename_file_or_dir:function(data){var that=this;that.is_editor=!0,$(".file_list_content .file_tr:nth-child("+(data.index+1)+")").addClass("editr_tr").find(".file_title").empty().append($("icon"==bt.get_cookie("rank")?'":'')),"icon"==bt.get_cookie("rank")&&$("textarea[name=rename_file_input]").css({height:$("textarea[name=rename_file_input]")[0].scrollHeight}),$(("icon"==bt.get_cookie("rank")?"textarea":"input")+"[name=rename_file_input]").on("input",(function(){if("icon"==bt.get_cookie("rank")&&(this.style.height="auto",this.style.height=this.scrollHeight+"px"),"file"==data.type){var ext_arry=$(this).val().split("."),ext=ext_arry[ext_arry.length-1];$(this).parent().prev().find(".file_icon").removeAttr("class").addClass("file_icon file_"+ext)}})).keyup((function(e){13==e.keyCode&&$(this).blur(),e.stopPropagation(),e.preventDefault()})).blur((function(){var _val=$(this).val().replace(/[\r\n]/g,""),config={sfile:data.path,dfile:that.path_resolve(that.file_path,_val)};return data.filename==_val||""==_val?($(".file_list_content .file_tr:nth-child("+(data.index+1)+")").removeClass("editr_tr").find(".file_title").empty().append($(""+data.filename+"")),that.is_editor=!1,!1):that.match_unqualified_string(_val)?layer.msg('The name cannot have /\\:*?"<>| symbol',{icon:2}):(that.rename_file_req(config,(function(res){that.reader_file_list({path:that.file_path},(function(){layer.msg(res.msg,{icon:res.status?1:2})}))})),void(that.is_editor=!1))})).focus()},set_file_share:function(data){var that=this;this.loadY=bt.open({type:1,shift:5,closeBtn:2,area:"450px",title:"Set share "+data.type_tips+"-["+data.filename+"]",btn:["Create","Cancel"],content:'
          Share name
          Expiration date
          Extraction code
          ',yes:function(indexs,layers){var ps=$("[name=ps]").val(),expire=$("[name=expire]:checked").val(),password=$("[name=password]").val();if(""===ps)return layer.msg("No sharing name!",{icon:2}),!1;that.create_download_url({filename:data.path,ps:ps,password:password,expire:expire},(function(res){if(!res.status)return layer.msg(res.msg,{icon:res.status?1:2}),!1;var rdata=res.msg;that.file_list[data.index]=$.extend(that.file_list[data.index],{down_id:rdata.id,down_info:rdata}),that.loadY.close(),that.info_file_share(data),that.reader_file_list_content(that.file_list)}))},success:function(layers,index){$("#random_paw").click((function(){$(this).prev().val(bt.get_random(6))}))}})},info_file_share:function(data){var that=this;if(void 0===data.down_info)return this.get_download_url_find({id:data.down_id},(function(res){that.file_list[data.index]=$.extend(that.file_list[data.index],{down_info:res}),that.file_share_view(that.file_list[data.index],"fonticon")})),!1;this.file_share_view(data,"fonticon")},file_share_view:function(datas,type){var data=datas;"fonticon"==type&&(data=datas.down_info);var that=this,download_url=location.origin+"/down/"+data.token;this.loadY=bt.open({type:1,shift:5,closeBtn:2,area:"550px",title:"Share details-["+data.filename+"]",content:'
          Share name
          Expiration date
          '+(data.expire>new Date("2099-01-01 00:00:00").getTime()/1e3?'Permanent':bt.format_data(data.expire))+'
          ',success:function(layers,index){var copy_url=new ClipboardJS(".copy_url"),copy_paw=new ClipboardJS(".copy_paw");copy_url.on("success",(function(e){layer.msg("Copy link succeeded!",{icon:1}),e.clearSelection()})),copy_paw.on("success",(function(e){layer.msg("Copy link and extraction code succeeded!",{icon:1}),e.clearSelection()})),$(".layer_close").click((function(){layer.close(index)})),$(".QR_code").click((function(){layer.closeAll("tips"),layer.tips('
          ',".QR_code",{area:["150px","150px"],tips:[1,"#ececec"],time:0,shade:[.05,"#000"],shadeClose:!0,success:function(){jQuery("#QR_code").qrcode({render:"canvas",text:download_url,height:130,width:130})}})})),$(".close_down").click((function(){that.remove_download_url({id:data.id,fileName:data.filename},(function(res){that.loadY.close(),"fonticon"==type&&(that.file_list[datas.index].down_id=0,that.reader_file_list_content(that.file_list)),"list"==type&&that.render_share_list(),layer.msg(res.msg,{icon:res.status?1:2})}))}))}})},del_file_or_dir:function(data){var that=this;that.is_recycle?bt.confirm({title:"Delete "+data.type_tips+"[ "+data.filename+" ]",msg:"Comfirm delete "+data.type_tips+"[ "+data.path+" ],it will move to recycle bin after delete, continue?"},(function(){that.del_file_req(data,(function(res){that.reader_file_list({path:that.file_path}),layer.msg(res.msg,{icon:res.status?1:2})}))})):bt.show_confirm("Delete "+data.type_tips+"[ "+data.filename+" ]",'Recycle bin is not currently open, delete '+("dir"==data.type?"directory":"file")+" cannot be restored after, continue?
          ",(function(){that.del_file_req(data,(function(res){that.reader_file_list({path:that.file_path}),layer.msg(res.msg,{icon:res.status?1:2})}))}))},cancel_file_favorites:function(data){var that=this,index=data.index;this.loadY=bt.confirm({title:data.filename+lan.files.unfavorite1,msg:lan.files.unfavorite1+" ["+data.path+"] "+lan.files.continue},(function(){that.$http("del_files_store",{path:data.path},(function(res){res.status&&(that.file_list[index].caret=!1,that.reader_file_list_content(that.file_list),that.load_favorites_index_list()),layer.msg(res.msg,{icon:res.status?1:2})}))}))},set_soft_link:function(data){var that=this;bt_tools.open({title:"Create Softlink",area:"520px",content:{class:"pd20",formLabelWidth:"110px",form:[{label:"Source file",group:{type:"text",name:"sfile",width:"280px",placeholder:"Please select the folder and file to be linked",icon:{type:"glyphicon-folder-open",event:function(ev){},select:"all"},value:"",input:function(ev){}}},{label:"Softlink name",group:{type:"text",name:"name",width:"280px",placeholder:"Please enter the name of the softlink",value:""}}]},init:function(){var e=null,t=setInterval((function(){if($('input[name="sfile"]').length<1&&clearInterval(t),e!=$('input[name="sfile"]').val()){var i=(e=$('input[name="sfile"]').val()).split("/");i.length>1&&$('[name="name"]').val(i[i.length-1])}}),100)},yes:function(e,i,a){delete(e=$.extend(e,{dfile:that.file_path+"/"+e.name})).name,bt_tools.send("files/CreateLink",e,(function(e){e.status&&(layer.close(i),bt.msg(e),that.reader_file_list())}),{tips:"Create Softlink"})}})},set_file_authority:function(data,isPatch){var that=this;that.get_file_authority({path:data.path},(function(rdata){var tex='
          ';that.loadY=layer.open({type:1,closeBtn:2,title:lan.files.set_auth+"["+data.filename+"]",area:"465px",shadeClose:!1,content:'
          Set permission
          Backups list
          '+tex+"
          "+lan.files.file_own+'

          '+lan.files.file_read+'

          '+lan.files.file_write+'

          '+lan.files.file_exec+"

          "+lan.files.file_group+'

          '+lan.files.file_read+'

          '+lan.files.file_write+'

          '+lan.files.file_exec+"

          "+lan.files.file_public+'

          '+lan.files.file_read+'

          '+lan.files.file_write+'

          '+lan.files.file_exec+'

          '+lan.files.file_menu_auth+", "+lan.files.file_own+'
          ",success:function(index,layers){that.edit_access_authority(),$("#access").keyup((function(){that.edit_access_authority()})),$("input[type=checkbox]").change((function(){for(var idName=["owner","group","public"],onacc="",n=0;n

          Please enter the current backup name

          Remarks
          ',yes:function(layerss,index){$(".layer_close").click((function(){layer.close(index)})),that.$http("back_path_permissions",{back_sub_dir:sub_type,path:file,remark:$("input.form-control").val()},(function(edata){if(layer.close(layerss),!edata.status)return layer.msg(edata.msg,{time:1900,icon:2}),!1;if(0==type){var new_back=that.backup_list();$(".buplist").html(new_back)}else if(3==type){var chmod=$("#access").val(),chown=$("#chown").val(),all=$("#accept_all").prop("checked")?"True":"False",data="filename="+encodeURIComponent(file)+"&user="+chown+"&access="+chmod+"&all="+all;Oksend(data),layer.closeAll()}layer.msg(edata.msg,{time:1900,icon:1})}))},btn2:function(){layer.close(layerss)},cancel:function(){layer.close(layerss)}})},backup_list:function(){var all_back="",that=this;this.$http("get_all_back",(function(edata){for(var i=0;i '+date+' "+edata[i][3]+' '+edata[i][1]+' Del '}$(".allback .buplist").html(all_back)}))},manage_backup:function(){var that=this,layerss=layer.open({type:1,closeBtn:2,title:"Manage Backups",area:["630px","500px"],shadeClose:!1,content:'
          Backup time Backup path Name Delect
          ',cancel:function(){layer.close(layerss)},success:function(){that.backup_list(),$("#btn-backup").click((function(e){var backup_path=$("#server_path").val();that.backup_files_permission(backup_path,0)})),$(".allback").on("click",".del_back",(function(){var _id=$(this).parents("tr").attr("data-id");that.del_backup(_id)}))}})},del_backup:function(id){var that=this;layer.confirm("The backup cannot be restored after deletion.
          Continue to delete?",{title:"Confirm delete?",btn:["Yes","No"],closeBtn:2},(function(index,layero){that.$http("del_path_premissions",{id:id},(function(edata){var file=$(".layui-layer-title:eq(0)").text();"Manage Backups"!==file?(file=$(".backup_list").attr("data-path"),that.backup_files_list(file)):$(".allback tbody tr[data-id="+id+"]").remove(),layer.msg(edata.msg,{icon:edata.status?1:2})}))}))},backup_files_list:function(fileName){var that=this;that.$http("get_path_premissions",{path:fileName},(function(edata){var tbody="",cont="";if(0==edata.length)tbody='No data';else for(var i=0;i '+edata[i][4]+" "+edata[i][2]+" "+edata[i][1]+" "+date+' Restore | Del '}cont='
          Fix all permissions to [ Folder: 755, File: 644 ]
          '+tbody+"
          Name permission Owner Backup Time Opt
          ",$(".chmodset").hide(),$(".backup_lists").html(cont),$(".fixper").click((function(){layer.confirm("Note: Under the file or folder all permissions will be fixed to [ Folder: 755, File: 644 ]",{title:"Fix Permissions?",btn:["Confirm","Cancel"],closeBtn:2},(function(index,layero){that.$http("fix_permissions",{path:fileName},(function(res){layer.closeAll(),that.reader_file_list({path:that.file_path,is_operating:!1}),layer.msg(res.msg,{icon:res.status?1:2})}))}))})),$(".restore_backup").click((function(){that.restore_file_permission($(this).attr("data-time"),fileName)})),$(".backup_list").on("click",".del_back",(function(){var _id=$(this).attr("data-id");that.del_backup(_id)}))}))},restore_file_permission:function(restore_time,fileName){var that=this,sub_type=$("#accept_all").prop("checked")?1:0,layerss=layer.open({type:1,closeBtn:2,title:"Confirm restore",area:"330px",shadeClose:!1,btn:["Yes","No"],content:'
          Restore would overwrite the current settings, continue?
          ',yes:function(layerss,index){that.$http("restore_path_permissions",{restore_sub_dir:sub_type,date:restore_time,path:fileName},(function(edata){layer.closeAll(),layer.msg(edata.msg,{icon:1,time:1900})}))},btn2:function(){layer.close(layerss)},cancel:function(){layer.close(layerss)}})},get_present_task_view:function(){this.file_present_task=layer.open({type:1,title:"Real-time task queue",area:"500px",closeBtn:2,skin:"present_task_list",shadeClose:!1,shade:!1,offset:"auto",content:'
          '})},render_present_task_list:function(){var that=this;this.get_task_req({status:-3},(function(lists){if(0==lists.length)return layer.close(that.file_present_task),that.file_present_task=null,void that.reader_file_list({path:that.file_path,is_operating:!1});var task_body="",is_add=!1;$.each(lists,(function(index,item){-1==item.status?(that.file_present_task||that.get_present_task_view(),"1"==item.type?task_body+='
          • '+lan.layout.download+item.shell+''+item.log.pre+'%'+lan.public.cancel+'
            '+item.log.used+"/"+ToSize(item.log.total)+''+(0==item.log.speed?lan.layout.connect:item.log.speed)+'/s'+lan.files.expected_to_be+": "+item.log.time+"
          ":task_body+='
          '+item.name+": "+item.shell+''+lan.public.cancel+'
          '+item.log+"
          "):(is_add||(task_body+='
          '+lan.layout.wait_task+'
            ',is_add=!0),task_body+='
          • '+item.name+": "+item.shell+'X
          • ')})),that.file_present_task&&(is_add&&(task_body+="
          "),$(".message-list").html(task_body)),setTimeout((function(){that.render_present_task_list()}),1e3)}))},remove_present_task:function(id){var that=this;layer.confirm("Do you want to cancel the upload of files? It need to delete the uploaded files manually. Continue?",{title:"Cancel file upload",icon:0},(function(indexs){bt.send("remove_task","task/remove_task",{id:id},(function(rdata){layer.msg(rdata.msg,{icon:1}),layer.close(that.file_present_task),that.file_present_task=null})),layer.close(indexs)}))},edit_access_authority:function(){for(var access=$("#access").val(),idName=["owner","group","public"],n=0;nidName.length))switch(onacc>7&&$("#access").val(access.substr(0,access.length-1)),onacc){case"1":$("#"+idName[i]+"_x").prop("checked",!0);break;case"2":$("#"+idName[i]+"_w").prop("checked",!0);break;case"3":$("#"+idName[i]+"_x").prop("checked",!0),$("#"+idName[i]+"_w").prop("checked",!0);break;case"4":$("#"+idName[i]+"_r").prop("checked",!0);break;case"5":$("#"+idName[i]+"_r").prop("checked",!0),$("#"+idName[i]+"_x").prop("checked",!0);break;case"6":$("#"+idName[i]+"_r").prop("checked",!0),$("#"+idName[i]+"_w").prop("checked",!0);break;case"7":$("#"+idName[i]+"_r").prop("checked",!0),$("#"+idName[i]+"_w").prop("checked",!0),$("#"+idName[i]+"_x").prop("checked",!0)}}},get_file_authority:function(data,callback){this.$http("GetFileAccess",{filename:data.path},(function(rdata){callback&&callback(rdata)}))},set_dir_kill:function(data){var that=this;"php"==data.ext?that.$http("file_webshell_check",{filename:data.path},(function(rdata){layer.msg(rdata.msg,{icon:rdata.status?1:2})})):layer.confirm("目录查杀将包含子目录中的php文件,是否操作?",{title:"目录查杀["+data.filename+"]",closeBtn:2,icon:3},(function(index){that.$http("dir_webshell_check",{path:data.path},(function(rdata){layer.msg(rdata.msg,{icon:rdata.status?1:2})}))}))},path_resolve:function(paths,param){var path="",split="";return Array.isArray(param)||(param=[param]),paths.replace(/([\/|\/]*)$/,(function($1){return split=$1,"www"})),$.each(param,(function(index,item){path+="/"+item})),(paths+path).replace("//","/")},get_ext_name:function(fileName){for(var extArr=fileName.split("."),exts=["folder","folder-unempty","sql","c","cpp","cs","flv","css","js","htm","html","java","log","mht","php","url","xml","ai","bmp","cdr","gif","ico","jpeg","jpg","JPG","png","psd","webp","ape","avi","mkv","mov","mp3","mp4","mpeg","mpg","rm","rmvb","swf","wav","webm","wma","wmv","rtf","docx","fdf","potm","pptx","txt","xlsb","xlsx","7z","cab","iso","rar","zip","gz","bt","file","apk","bookfolder","folder-empty","fromchromefolder","documentfolder","fromphonefolder","mix","musicfolder","picturefolder","videofolder","sefolder","access","mdb","accdb","fla","doc","docm","dotx","dotm","dot","pdf","ppt","pptm","pot","xls","csv","xlsm"],extLastName=extArr[extArr.length-1],i=0;i')},yes:function(){var ress;if(""==form.getVal().dfile)return layer.msg("Please select a valid address",{icon:2});form.submitForm((function(res,datas){setTimeout((function(){that.reader_file_list({path:datas.path})}),1e3),null!=res&&null!=res||layer.msg(lan.files.zip_ok,{icon:1}),res.status&&that.render_present_task_list(),layer.close(loadT)}))}})}))},unpack_file_to_path:function(data){var that=this,_type="zip",spath="";spath=data.path.substring(0,data.path.lastIndexOf("/")),this.reader_form_line({url:"UnZip",overall:{width:"310px"},data:[{label:"File name",name:"z_name",placeholder:"Compress file name",value:data.path},{label:"Compress path",name:"z_path",placeholder:"Compress path",value:spath},{label:"Encoding",name:"z_code",type:"select",value:"UTF-8",list:[["UTF-8","UTF-8"],["gb18030","GBK"]]}],beforeSend:function(updata){return{sfile:updata.z_name,dfile:updata.z_path,type:_type,coding:updata.z_code,password:updata.z_password}}},(function(form,html){var loadT=layer.open({type:1,title:"Decompress file",area:"520px",shadeClose:!1,closeBtn:2,skin:"unpack_file_view",btn:["Comfirm","Cancel"],content:html[0].outerHTML,success:function(){"gz"==data.ext&&(_type="tar"),"zip"==_type&&$(".unpack_file_view .line:nth-child(2)").append('
          Password
          ')},yes:function(){var ress=form.getVal();return""==ress.z_name?layer.msg("Please enter the file name path",{icon:2}):""==ress.z_path?layer.msg("Please enter the decompression address",{icon:2}):void form.submitForm((function(res,datas){layer.close(loadT),setTimeout((function(){that.reader_file_list({path:datas.path})}),1e3),res.status&&that.render_present_task_list(),layer.msg(res.msg,{icon:res.status?1:2})}))}})}))},match_unqualified_string:function(item){var containSpecial;return RegExp(/[(\ )(\*)(\|)(\\)(\:)(\")(\/)(\<)(\>)(\?)(\)]+/).test(item)},reader_form_line:function(config,callback){var that=this,random=bt.get_random(10),html=$('
          '),data=config,eventList=[],that=this;Array.isArray(config)||(data=config.data),$.each(data,(function(index,item){var labelWidth=item.labelWidth||config.overall.labelWidth||null,event_random=bt.get_random(10),width=item.labelWidth||config.overall.width||null,form_line=$('
          "+(item.label||"")+'
          '),form_el=$(function(){switch(item.type){case"select":return"";case"text":default:return"'}}(item));(item.eventType||item.event)&&(Array.isArray(item.eventType)||(item.eventType=[item.eventType]),$.each(item.eventType,(function(index,items){if(eventList.push({el:event_random,type:items||"click",event:item[items]||null}),config.el){var els=$('[data-event="'+item.el+'"]');if(item[items])"enter"==items?els.on("keyup",(function(e){13==e.keyCode&&item.event(e)})):els.on(item||"click",item.event);else if("focus"==items){var vals=els.val();""!=vals&&els.val("").focus().val(vals)}else els[items]()}}))),form_line.find(".info-r").append(form_el),html.append(form_line)})),config.el&&$(config.el).empty().append(html),callback&&callback({getVal:function(){return $("#"+random).serializeObject()},setEvent:function(){$.each(eventList,(function(index,item){var els=$('[data-event="'+item.el+'"]');if(null===item.event)if("focus"==item.type){var vals=els.val();""!=vals&&els.val("").focus().val(vals)}else els[item.type]();else"enter"==item.type?els.on("keyup",(function(e){13==e.keyCode&&item.event(e)})):els.on(item.type,item.event)}))},submitForm:function(callback){var data=this.getVal();config.beforeSend&&(data=config.beforeSend(data)),that.loadT=bt.load("submitting the form, please wait..."),bt.send(config.url,"files/"+config.url,data,(function(rdata){that.loadT.close(),callback&&callback(rdata,data)}))}},html)},$http:function(data,parem,callback){var that=this,loadT="";"string"==typeof data?("object"!=typeof parem&&(callback=parem,parem={}),Array.isArray(this.method_list[data])||(this.method_list[data]=["files",this.method_list[data]]),this.$http({method:data,tips:!!this.method_list[data][1]&&this.method_list[data][1],module:this.method_list[data][0],data:parem,msg:!0},callback)):(void 0!==data.tips&&data.tips&&(loadT=bt.load(data.tips)),bt.send(data.method,(data.module||"files")+"/"+data.method,data.data||{},(function(res){if(""!=loadT&&loadT.close(),"string"==typeof res&&(res=JSON.parse(res)),!1===res.status&&res.msg)return bt.msg(res),!1;parem&&parem(res)})))}};bt_file.init(),Function.prototype.delay=function(that,arry,time){return Array.isArray(arry)||(time=arry,arry=[]),void 0===time&&(time=0),setTimeout(this.apply(that,arry),time),this},jQuery.prototype.serializeObject=function(){var a,o,h,i,e;for(a=this.serializeArray(),h=(o={}).hasOwnProperty,i=0;i-1){return true}return false})(),init:function(){if($("#mask_layer").length==0){window.UploadFiles=function(){bt_file.file_drop.dialog_view()};$("body").append($('"));this.event_relation(document.querySelector("#container"),document,document.querySelector("#mask_layer"))}},event_relation:function(f,b,a){var d=this,e=Object.keys(arguments);for(var c in arguments){if(typeof arguments[c]=="object"&&typeof arguments[c].nodeType!="undefined"){arguments[c]={el:arguments[c],callback:null}}}b.el.addEventListener("dragleave",(b.callback!=null)?b.callback:function(g){if(g.x==0&&g.y==0){$("#mask_layer").hide()}g.preventDefault()},false);f.el.addEventListener("dragenter",(f.callback!=null)?f.callback:function(g){if(g.dataTransfer.items[0].kind=="string"){return false}$("#mask_layer").show();d.isLayuiDrop=false;g.preventDefault()},false);a.el.addEventListener("dragover",function(g){g.preventDefault()},false);a.el.addEventListener("drop",(f.callback!=null)?a.callback:d.ev_drop,false)},ev_drop:function(g){if(g.dataTransfer.items[0].kind=="string"){return false}if(!bt_file.file_drop.is_webkit){$("#mask_layer").hide();return false}g.preventDefault();if(bt_file.file_drop.uploading){layer.msg("正在上传文件中,请稍候...");return false}var a=g.dataTransfer.items,f,c=0;loadT=layer.msg("正在获取上传文件信息,请稍候...",{icon:16,time:0,shade:0.3});bt_file.file_drop.isUpload=true;if(a&&a.length&&a[0].webkitGetAsEntry!=null){if(a[0].kind!="file"){return false}}if(bt_file.file_drop.filesList==null){bt_file.file_drop.filesList=[]}for(var d=bt_file.file_drop.filesList.length-1;d>=0;d--){if(bt_file.file_drop.filesList[d].is_upload){bt_file.file_drop.filesList.splice(-d,1)}}$("#mask_layer").hide();function b(e){e.getFilesAndDirectories().then(function(i){return h(i,e.path)})}var h=function(k,j){if(!bt_file.file_drop.isUpload){return false}for(var e=0;ebt_file.file_drop.isUploadNumber){bt_file.file_drop.isUpload=false;layer.msg(" "+bt_file.file_drop.isUploadNumber+"份,无法上传,请压缩后上传!。",{icon:2,area:"405px"});bt_file.file_drop.filesList=[];clearTimeout(f);return false}bt_file.file_drop.filesList.push({file:k[e],path:bt.get_file_path(j+"/"+k[e].name).replace("//","/"),name:k[e].name.replace("//","/"),icon:bt_file.get_ext_name(k[e].name),size:bt_file.file_drop.to_size(k[e].size),upload:0,is_upload:false});bt_file.file_drop.uploadAllSize+=k[e].size;clearTimeout(f);f=setTimeout(function(){layer.close(loadT);bt_file.file_drop.dialog_view()},100);c++}}};if("getFilesAndDirectories" in g.dataTransfer){g.dataTransfer.getFilesAndDirectories().then(function(e){return h(e,"/")})}},dialog_view:function(a){var f=this,c="";this.f_path=bt_file.file_path;if(!$(".file_dir_uploads").length>0){if(f.filesList==null){f.filesList=[]}for(var b=0;b
          '+(e.path+"/"+e.name).replace("//","/")+''+e.size+''+f.is_upload_status(e.upload)+'
          '}var d=f.filesList.length>11;layer.open({type:1,closeBtn:1,maxmin:true,area:["650px","605px"],btn:["开始上传","取消上传"],title:"上传文件到【"+bt.get_cookie("Path")+"】--- 支持断点续传",skin:"file_dir_uploads",content:'
          '+(c!=""?('
          • 文件名文件大小上传状态
            '+c+"
          "):""+(!f.is_webkit?'当前浏览器暂不支持拖动上传,推荐使用Chrome浏览器或WebKit内核的浏览器。':"请将需要上传的文件拖到此处")+"")+"
          ",success:function(){$("#mask_layer").hide();$(".file_dir_uploads .layui-layer-max").hide();$(".upload_btn_groud .upload_file_btn").click(function(){$(".upload_btn_groud .dropdown-menu [data-type=file]").click()});$(".upload_btn_groud .dropdown-menu a").click(function(){var h=$(this).attr("data-type");$('").change(function(n){var l=n.target.files,m=[];for(var k=0;k
        • 文件名文件大小上传状态
          • ').removeClass("active");if(Array.isArray(a)){for(var b=0;b
            '+(e.path+"/"+e.name).replace("//","/")+''+e.size+''+f.is_upload_status(e.upload)+'
            '}$(".dropUpLoadFile").append(c)}else{$(".dropUpLoadFile").append('
          • '+(a.path+"/"+a.name).replace("//","/")+''+a.size+''+f.is_upload_status(a.upload)+'
          • ')}}},is_upload_status:function(a,b){if(b===undefined){b=""}switch(a){case -1:return'上传失败'+(b!=""?","+b:"")+"";break;case 0:return'等待上传';break;case 1:return'上传成功';break;case 2:return'上传中'+b+"";break;case 3:return'已暂停';break}},set_upload_view:function(b,a){var e=$(".dropUpLoadFile li:eq("+b+")"),d=this;var c=$(".file_upload_info");if($(".file_upload_info .uploadProgress").length==0){$(".file_upload_info").html('总进度 ,正在上传 ,上传失败 上传速度 获取中,预计上传时间 获取中')}c.show().prev().hide().parent().css("paddingRight",0);if(d.errorLength>0){c.find(".uploadError").text("("+d.errorLength+"份)").parent().show()}c.find(".uploadNumber").html("("+d.uploadLength+"/"+d.filesList.length+")");c.find(".uploadProgress").html(((d.uploadedSize/d.uploadAllSize)*100).toFixed(2)+"%");if(a.upload===1||a.upload===-1){d.filesList[b].is_upload=true;d.uploadLength+=1;e.find(".fileLoading").css({width:"100%",opacity:".5",background:a.upload==-1?"#ffadad":"#20a53a21"});e.find(".filesize").text(a.size);e.find(".fileStatus").html(d.is_upload_status(a.upload,(a.upload===1?("(耗时:"+d.diff_time(d.startTime,d.endTime)+")"):a.errorMsg)));e.find(".fileLoading").fadeOut(500,function(){$(this).remove();var f=$(".dropUpLoadFile");if(f.length==0){return false}if(f[0].scrollHeight>f.height()){f.scrollTop(f.scrollTop()+40)}})}else{e.find(".fileLoading").css("width",a.percent);e.find(".filesize").text(a.upload_size+"/"+a.size);e.find(".fileStatus").html(d.is_upload_status(a.upload,"("+a.percent+")"))}},clear_drop_stauts:function(a){var f=new Date(),c=this;if(!a){try{var b=bt_file.file_drop.to_size(bt_file.file_drop.uploadedSize/((f.getTime()-bt_file.file_drop.initTimer.getTime())/1000));$(".file_upload_info").html("上传成功 "+this.uploadLength+"个文件,"+(this.errorLength>0?("上传失败 "+this.errorLength+"个文件,"):"")+"耗时"+this.diff_time(this.initTimer,f)+",平均速度 "+b+"/s").append($('').click(function(){$(".file_upload_info").hide().prev().show()}))}catch(d){}}$(".layui-layer-btn0").removeAttr("style data-upload").text("开始上传");$.extend(bt_file.file_drop,{startTime:0,endTime:0,uploadLength:0,splitSize:1024*1024*2,filesList:[],errorLength:0,isUpload:false,isUploadNumber:800,uploadAllSize:0,uploadedSize:0,topUploadedSize:0,uploadExpectTime:0,initTimer:0,speedInterval:null,timerSpeed:0,uploading:false});clearInterval(c.speedInterval)},upload_file:function(b,a){if(b==undefined&&this.uploadSuspend.length==0){b=0,a=0}if(this.filesList.length===a){clearInterval(this.speedInterval);this.clear_drop_stauts();bt_file.reader_file_list({path:bt_file.file_path,is_operating:false});return false}var d=this;d.splitEndTime=new Date().getTime();d.get_timer_speed();d.splitStartTime=new Date().getTime();var c=this.filesList[a],e="";if(c==undefined){return false}e=Math.min(c.file.size,b+this.splitSize),d.fileSize=e-b;form=new FormData();if(b==0){d.startTime=new Date();c=$.extend(c,{percent:"0%",upload:2,upload_size:"0B"})}form.append("f_path",this.f_path+c.path);form.append("f_name",c.name);form.append("f_size",c.file.size);form.append("f_start",b);form.append("blob",c.file.slice(b,e));d.set_upload_view(a,c);$.ajax({url:"/files?action=upload",type:"POST",data:form,async:true,processData:false,contentType:false,success:function(f){if(typeof(f)==="number"){d.set_upload_view(a,$.extend(c,{percent:(((f/c.file.size)*100).toFixed(2)+"%"),upload:2,upload_size:d.to_size(f)}));if(e!=f){d.uploadedSize+=f}else{d.uploadedSize+=parseInt(e-b)}d.upload_file(f,a)}else{if(f.status){d.endTime=new Date();d.uploadedSize+=parseInt(e-b);d.set_upload_view(a,$.extend(c,{upload:1,upload_size:c.size}));d.upload_file(0,a+=1)}else{d.set_upload_view(a,$.extend(c,{upload:-1,errorMsg:f.msg}));d.errorLength++}}},error:function(f){if(d.filesList[a].req_error===undefined){d.filesList[a].req_error=1}if(d.filesList[a].req_error>2){d.set_upload_view(a,$.extend(d.filesList[a],{upload:-1,errorMsg:f.statusText=="error"?"网络中断":f.statusText}));d.errorLength++;d.upload_file(b,a+=1);return false}d.filesList[a].req_error+=1;d.upload_file(b,a)}})},get_timer_speed:function(e){var f=new Date().getTime();if(f-this.speedLastTime>1000){var d=this,c=0;if(e==undefined){e=200}var b=(d.splitEndTime-d.splitStartTime)/1000;d.timerSpeed=(d.fileSize/b).toFixed(2);d.updateedSizeLast=d.uploadedSize;if(d.timerSpeed<2){return}$(".file_upload_info .uploadSpeed").text(d.to_size(isNaN(d.timerSpeed)?0:d.timerSpeed)+"/s");var a=d.time(parseInt(((d.uploadAllSize-d.uploadedSize)/d.timerSpeed)*1000));if(!isNaN(d.timerSpeed)){$(".file_upload_info .uploadEstimate").text(a.indexOf("NaN")==-1?a:"0秒")}this.speedLastTime=f}},time:function(c){var b=Math.floor(c/(60*60*1000));var d=Math.floor(c/(60*1000));var e=parseInt((c%(60*1000))/1000);var a=e+"秒";if(d>0){a=d+"分钟"+e+"秒"}if(b>0){a=b+"小时"+Math.floor((c-(b*(60*60*1000)))/(60*1000))+"分钟"}return a},diff_time:function(b,g){var e=g.getTime()-b.getTime();var c=Math.floor(e/(60*1000));var d=e%(60*1000);var f=d/1000;var a=f.toFixed(c>0?0:2)+"秒";if(c>0){a=c+"分"+f.toFixed(0)+"秒"}return a},to_size:function(f){var i=[" B"," KB"," MB"," GB"," TB"," PB"];var h=1024;for(var c=0;c1){a.file_groud_event({open:"soft_link"});return false}var c=$(this).data("type"),b=$(".create_file_or_dir .nav_down_list");b.css({display:function(){setTimeout(function(){b.removeAttr("style")},100);return"none"}});if(!a.is_editor){a.is_editor=true;$(".file_list_content").prepend('
            '+(bt.get_cookie("rank")=="icon"?'":'')+"
            ");$((bt.get_cookie("rank")=="icon"?"textarea":"input")+"[name=createArea]").on("input",function(){if(bt.get_cookie("rank")=="icon"){this.style.height="auto";this.style.height=this.scrollHeight+"px"}}).keyup(function(f){if(f.keyCode==13){$(this).blur()}}).blur(function(f){var g=$(this).val().replace(/[\r\n]/g,"");if(a.match_unqualified_string(g)){return layer.msg('名称不能含有 /\\:*?"<>|符号',{icon:2})}if(g==""){g=(c=="newBlankDir"?"新建文件夹":"新建文件")}setTimeout(function(){a.create_file_req({type:(c=="newBlankDir"?"folder":"file"),path:a.file_path+"/"+g},function(e){if(e.status){a.reader_file_list({path:a.file_path})}layer.msg(e.msg,{icon:e.status?1:2})});$(".createModel").remove();a.is_editor=false},300);f.preventDefault()}).focus()}else{return false}d.stopPropagation();d.preventDefault()});$(".file_nav_view .favorites_file_path ul").on("click","li",function(h){var b=$(this).data("path"),f=$(this).data("type"),g=$(".favorites_file_path .nav_down_list");if(f=="dir"){a.reader_file_list({path:b,is_operating:true})}else{if($(this).data("null")!=undefined){return false}var c=$(this).attr("title").split("."),i=c[c.length-1],d=a.determine_file_type(i);switch(d){case"text":openEditorView(0,b);break;case"video":a.open_video_play(b);break;case"images":a.open_images_preview({filename:$(this).attr("title"),path:b});break;default:a.reader_file_list({path:a.retrun_prev_path(b),is_operating:true});break}}g.css({display:function(){setTimeout(function(){g.removeAttr("style")},100);return"none"}});h.stopPropagation();h.preventDefault()});$(".terminal_view").on("click",function(){web_shell()});$(".share_file_list").on("click",function(){a.open_share_view()});$(".mount_disk_list").on("click",".nav_btn",function(){var b=$(this).data("menu");a.reader_file_list({path:b,is_operating:true})});$(".mount_disk_list").on("click",".nav_down_list li",function(){var c=$(this).data("disk"),b=$(".mount_disk_list.thezoom .nav_down_list");b.css({display:function(){setTimeout(function(){b.removeAttr("style")},100);return"none"}});a.reader_file_list({path:c,is_operating:true})});$(".file_nav_view").on("click",".recycle_bin",function(b){a.recycle_bin_view();b.stopPropagation();b.preventDefault()});$(".file_nav_view .multi").on("click",".nav_btn_group",function(b){var c=$(this).data("type");if(typeof c!="undefined"){a.batch_file_manage(c)}b.stopPropagation();b.preventDefault()});$(".file_nav_view .multi").on("click",".nav_btn_group li",function(b){var c=$(this).data("type");a.batch_file_manage(c);b.stopPropagation();b.preventDefault()});$(".file_nav_view").on("click",".file_all_paste",function(){a.paste_file_or_dir()});$(".file_list_header").on("click",".file_name,.file_size,.file_mtime,.file_accept,.file_user",function(d){var f=$(this).attr("data-tid"),b=$(this).find(".icon_sort").hasClass("active"),c=$(this).hasClass("active");if(!$(this).find(".icon_sort").hasClass("active")&&$(this).hasClass("active")){$(this).find(".icon_sort").addClass("active")}else{$(this).find(".icon_sort").removeClass("active")}$(this).addClass("active").siblings().removeClass("active").find(".icon_sort").removeClass("active").empty();$(this).find(".icon_sort").html('');if(!c){b=true}bt.set_cookie("files_sort",f);bt.set_cookie("name_reverse",b?"True":"False");a.reader_file_list({reverse:b?"True":"False",sort:f});return false});$(".file_list_header .file_th").each(function(c,e){var b=bt.get_cookie("files_sort"),d=bt.get_cookie("name_reverse");if($(this).attr("data-tid")===b){$(this).addClass("active").siblings().removeClass("active").find(".icon_sort").removeClass("active").empty();$(this).find(".icon_sort").html('');if(d==="False"){$(this).find(".icon_sort").addClass("active")}}});$(".file_list_header .file_check").on("click",function(c){var b=parseInt($(this).data("checkbox"));switch(b){case 0:$(this).addClass("active").removeClass("active_2").data("checkbox",1);$(".file_list_content .file_tr").addClass("active").removeClass("active_2");$(".nav_group.multi").removeClass("hide");$(".file_menu_tips").addClass("hide");a.file_table_arry=a.file_list.slice();break;case 2:$(this).addClass("active").removeClass("active_2").data("checkbox",1);$(".file_list_content .file_tr").addClass("active");$(".nav_group.multi").removeClass("hide");$(".file_menu_tips").addClass("hide");a.file_table_arry=a.file_list.slice();break;case 1:$(this).removeClass("active active_2").data("checkbox",0);$(".file_list_content .file_tr").removeClass("active");$(".nav_group.multi").addClass("hide");$(".file_menu_tips").removeClass("hide");a.file_table_arry=[];break}a.calculate_table_active()});$(".file_list_content").on("click",".file_checkbox",function(f){var b=$(this).parents(".file_tr"),d=b.data("index"),c=b.data("filename");if(b.hasClass("active")){b.removeClass("active");a.remove_check_file(a.file_table_arry,"filename",c)}else{b.addClass("active");b.attr("data-filename",a.file_list[d]["filename"]);a.file_table_arry.push(a.file_list[d])}a.calculate_table_active();f.stopPropagation()});$(".file_list_content").scroll(function(b){if($(this).scrollTop()==($(this)[0].scrollHeight-$(this)[0].clientHeight)){$(this).prev().css("opacity",1);$(this).next().css("opacity",0)}else{if($(this).scrollTop()>0){$(this).prev().css("opacity",1)}else{if($(this).scrollTop()==0){$(this).prev().css("opacity",0);$(this).next().css("opacity",1)}}}});$(".file_table_view .file_list_content").on("click",".file_tr",function(b){if($(b.target).hasClass("foo_menu_title")||$(b.target).parents().hasClass("foo_menu_title")){return true}$(this).addClass("active").siblings().removeClass("active");a.file_table_arry=[a.file_list[$(this).data("index")]];a.calculate_table_active();b.stopPropagation();b.preventDefault()});$(".file_table_view .file_list_content").on("click",".file_name .iconfont",function(f){var c=$(this).parents(".file_tr"),b=c.data("index"),d=a.file_list[b];d.index=b;if($(this).hasClass("icon-share1")){a.info_file_share(d)}if($(this).hasClass("icon-favorites")){a.cancel_file_favorites(d)}f.stopPropagation()});$(".file_table_view .file_list_content").on("dblclick",".file_tr",function(d){var b=$(this).data("index"),c=a.file_list[b];if($(d.target).hasClass("file_check")||$(d.target).parents(".foo_menu").length>0||$(d.target).hasClass("set_file_ps")||a.is_editor){return false}if(c.type=="dir"){if(c.filename=="Recycle_bin"){return a.recycle_bin_view()}a.reader_file_list({path:a.file_path+"/"+c.filename,is_operating:true})}else{switch(c.open_type){case"text":openEditorView(0,c.path);break;case"video":a.open_video_play(c);break;case"images":a.open_images_preview(c);break;case"compress":a.unpack_file_to_path(c);break}}d.stopPropagation();d.preventDefault()});$(".file_table_view .file_list_content").on("click",".file_title i,.file_ico_type .file_icon",function(f){var c=$(this).parents(".file_tr"),b=c.data("index"),d=a.file_list[b];if(d.type=="dir"){if(d.filename=="Recycle_bin"){return a.recycle_bin_view()}a.reader_file_list({path:a.file_path+"/"+d.filename,is_operating:true})}else{layer.msg(d.open_type=="compress"?"双击解压文件":"双击文件编辑")}f.stopPropagation();f.preventDefault()});$(".file_list_content").on("contextmenu",function(b){if($(b.target).attr("name")=="createArea"||$(b.target).attr("name")=="rename_file_input"){return true}else{return false}});$(".selection_right_menu").on("contextmenu",function(b){return false});$(".file_list_content").on("mousedown",".file_tr",function(b){if(b.which===1&&($(b.target).hasClass("foo_menu_title")||$(b.target).parents().hasClass("foo_menu_title"))){a.render_file_groud_menu(b,this);$(b.target).parent().addClass("foo_menu_click");$(this).siblings().find(".foo_menu").removeClass("foo_menu_click");$(this).addClass("active").siblings().removeClass("active")}else{if(b.which===3&&!a.is_editor){if(a.file_table_arry.length>1){a.render_files_multi_menu(b)}else{a.render_file_groud_menu(b,this);$(".content_right_menu").removeAttr("style");$(this).addClass("active").siblings().removeClass("active")}}else{return true}}b.stopPropagation();b.preventDefault()});$(".filePage").on("change",".showRow",function(){var b=$(this).val();console.log(b);bt.set_storage("local","showRow",b);a.reader_file_list({showRow:b,p:1,is_operating:false})});$(".filePage").on("click","div:nth-child(2) a",function(c){var b=$(this).attr("href").match(/p=([0-9]+)$/)[1];a.reader_file_list({path:a.path,p:b});c.stopPropagation();c.preventDefault()});$(".file_list_content").on("click",".folder_size",function(c){var b=a.file_list[$(this).parents(".file_tr").data("index")],d=this;a.get_file_size({path:b.path},function(e){$(d).text(bt.format_size(e.size))});c.stopPropagation();c.preventDefault()});$(".filePage").on("click","#file_all_size",function(b){if(a.file_path==="/"){layer.tips('当前目录为系统根目录(/),执行获取文件大小将占用大量磁盘IO,将导致服务器运行缓慢。',this,{tips:[1,"red"],time:5000});return false}a.get_dir_size({path:a.file_path})});$(".file_list_content").on("mousedown",function(f){if($(f.target).hasClass("file_checkbox")||$(f.target).hasClass("file_check")||$(f.target).hasClass("icon-share1")||$(f.target).hasClass("icon-favorites")||f.target.localName=="i"||$(f.target).parents(".app_menu_group").length>0||$(f.target).hasClass("createModel")||$(f.target).hasClass("editr_tr")||$(f.target).attr("name")=="createArea"||$(f.target).attr("name")=="rename_file_input"||$(f.target).hasClass("set_file_ps")||a.is_editor){return true}if(f.which==3&&!a.is_editor){$(".selection_right_menu").removeAttr("style");a.render_file_all_menu(f,this);return true}$(".file_list_content").bind("mousewheel",function(){return false});var c=$(this),g=0,e=c.offset().top,b=c.offset().left;var d={top:f.clientY-$(this).offset().top,left:f.clientX-$(this).offset().left};$(document).unbind("mousemove").mousemove(function(o){var j={top:o.clientY-e>0&&o.clientY-e1?c.height():0),left:o.clientX-b>0&&o.clientX-b1?c.width():0)};var p={top:j.top>d.top?d.top:j.top,left:j.left>d.left?d.left:j.left};if(bt.get_cookie("rank")=="list"){p.top=p.top+40}var q=Math.min(Math.abs(j.left-d.left),b+c.width()-p.left);var m=Math.min(Math.abs(j.top-d.top),e+c.height()-p.top);if(o.clientY-e<0){var n=Math.abs(o.clientY-e);c.scrollTop(c.scrollTop()-n);if(c.scrollTop()!=0){g+=n}m=m+g}if(o.clientY-(e+c.height())>1){var k=o.clientY-(e+c.height());c.scrollTop(c.scrollTop()+k);if(c[0].scrollHeight-c[0].scrollTop!==c[0].clientHeight){g+=k}m=m+g;p.top=p.top-g}if(d.top==j.top||d.left==j.left){return true}a.enter_files_box().show().css({left:p.left+"px",top:p.top+"px",width:q+"px",height:m+"px"});var r=a.enter_files_box().offset().top;var s=a.enter_files_box().offset().left;var l=a.enter_files_box().offset().left+a.enter_files_box().width();var i=a.enter_files_box().offset().top+a.enter_files_box().height();$(c).find(".file_tr").each(function(v,w){var u=$(w).offset().top;var t=$(w).offset().left;var h=$(w).offset().top+$(w).height();var x=$(w).offset().left+$(w).width();if(bt.get_cookie("rank")=="icon"){if(x>=s&&t<=l&&h>=r&&u<=i){$(w).addClass("active")}else{$(w).removeClass("active")}}else{if(x>=s&&h>=r&&u<=i){$(w).addClass("active")}else{$(w).removeClass("active")}}})});$(document).on("mouseup",function(){var i=[];var k=a.enter_files_box().offset().top;var h=a.enter_files_box().offset().left;var l=a.enter_files_box().offset().left+a.enter_files_box().width();var j=a.enter_files_box().offset().top+a.enter_files_box().height();$(c).find(".file_tr").each(function(p,q){var o=$(q).offset().top;var n=$(q).offset().left;var m=$(q).offset().top+$(q).height();var r=$(q).offset().left+$(q).width();if(bt.get_cookie("rank")=="icon"){if(r>=h&&n<=l&&m>=k&&o<=j){i.push($(q).data("index"))}}else{if(r>=h&&m>=k&&o<=j){i.push($(q).data("index"))}}});a.render_file_selected(i);a.enter_files_box().remove();$(".file_list_content").unbind("mousewheel")});f.stopPropagation();f.preventDefault()});$(".file_list_content").on("blur",".set_file_ps",function(e){var b=$(this).parents(".file_tr").data("index"),d=a.file_list[b],f=$(this).val(),c=$(this).data("value"),g=this;if(f==c){return false}bt_tools.send("files/set_file_ps",{filename:d.path,ps_type:0,ps_body:f},function(h){$(g).data("value",f)},{tips:"设置文件/目录备注",tips:true})});$(".file_list_content").on("keyup",".set_file_ps",function(b){if(b.keyCode==13){$(this).blur()}b.stopPropagation()});$(".file_list_header").on("mousedown",".file_width_resize",function(f){return false;if(f.which==3){return false}var e=$(this),b=$(this).prev().offset().left,c=$(".file_list_header").innerWidth(),d=0;d=c-$(".file_main_title").data;$(document).unbind("mousemove").mousemove(function(h){var g=h.clientX-b;a.set_style_width(e.prev().data("tid"),g)});$(document).one("mouseup",e,function(g){$(document).unbind("mousemove")});f.stopPropagation();f.preventDefault()});$(".cut_view_model").on("click",function(){var b=$(this).data("type");$(".file_table_view").addClass(b=="icon"?"icon_view":"list_view").removeClass(b!="icon"?"icon_view":"list_view").scrollLeft(0);bt.set_cookie("rank",b);$(this).addClass("active").siblings().removeClass("active")});$(".file_list_content").on("click",".set_operation_group a",function(d){var e=$(this).parents(".file_tr").data(),b=$(this).data("type"),c=a.file_list[e.index];if(b=="more"){return true}c.open=b;c.index=e.index;c.type_tips=c.type=="file"?"文件":"目录";a.file_groud_event(c)})},enter_files_box:function(){if($("#web_mouseDrag").length==0){$("
            ",{id:"web_mouseDrag",style:["position:absolute; top:0; left:0;","border:1px solid #072246; background-color: #cce8ff;","filter:Alpha(Opacity=15); opacity:0.15;","overflow:hidden;display:none;z-index:9;"].join("")}).appendTo(".file_table_view")}return $("#web_mouseDrag")},clear_table_active:function(){this.file_table_arry=[];$(".file_list_header .file_check").removeClass("active active_2");$(".file_list_content .file_tr").removeClass("active app_menu_operation");$(".file_list_content .file_tr .file_ps .foo_menu").removeClass("foo_menu_click");$(".app_menu_group").remove()},calculate_table_active:function(){var b=this,a=$(".file_list_header .file_check");if(this.file_table_arry.length==0){a.removeClass("active active_2").data("checkbox",0)}else{if(this.file_table_arry.length==this.file_list.length){a.addClass("active").removeClass("active_2").data("checkbox",1)}else{a.addClass("active_2").removeClass("active").data("checkbox",2)}}if(this.file_table_arry.length>0){$(document).unbind("keydown").on("keydown",function(g){var f=g.keyCode,d=g.target.localName.toLowerCase(),c=window.navigator.userAgent.indexOf("Mac")>-1;if(d=="input"||d=="textarea"){return true}if(g.ctrlKey&&f==67){if(b.file_table_arry.length==1){b.file_groud_event($.extend(b.file_table_arry[0],{open:"copy"}));$(".file_all_paste").removeClass("hide")}else{if(b.file_table_arry.length>1){b.batch_file_manage("copy")}}}if(g.ctrlKey&&f==88){if(b.file_table_arry.length==1){b.file_groud_event($.extend(b.file_table_arry[0],{open:"shear"}));$(".file_all_paste").removeClass("hide")}else{if(b.file_table_arry.length>1){b.batch_file_manage("shear")}}}});if(this.file_table_arry.length>1){$(".nav_group.multi").removeClass("hide");$(".file_menu_tips").addClass("hide")}else{$(".nav_group.multi").addClass("hide");$(".file_menu_tips").removeClass("hide")}}else{$(".nav_group.multi").addClass("hide");$(".file_menu_tips.multi").removeClass("hide");$(document).unbind("keydown")}$(".selection_right_menu,.file_path_input .file_dir_item .nav_down_list").removeAttr("style");b.set_menu_line_view_resize()},set_dir_view_resize:function(){var c=$(".file_path_input"),a=$(".file_path_input .file_dir_view"),e=a.attr("data-width"),d=null;if(e){parseInt(e)}else{e=a.width();a.attr("data-width",e)}if(a.width()-e<90){var b=0;$($(".file_path_input .file_dir_view .file_dir_item").toArray().reverse()).each(function(){var f=0;if(!$(this).attr("data-width")){$(this).attr("data-width",$(this).width());f=$(this).width()}else{f=parseInt($(this).attr("data-width"))}b+=f;if((c.width()-b)<=90){$(this).addClass("hide")}else{$(this).removeClass("hide")}})}var d=a.children(".file_dir_item.hide").clone(true);if(a.children(".file_dir_item.hide").length==0){c.removeClass("active").find(".file_dir_omit").addClass("hide")}else{d.each(function(){if($(this).find(".glyphicon-hdd").length==0){$(this).find(".file_dir").before('')}});c.addClass("active").find(".file_dir_omit").removeClass("hide");c.find(".file_dir_omit .nav_down_list").empty().append(d);c.find(".file_dir_omit .nav_down_list .file_dir_item").removeClass("hide")}},set_menu_line_view_resize:function(){var b=$(".file_nav_view").width(),c=0,f=0,a=0,d=$(".mount_disk_list"),e=$(".nav_group.multi");if(!d.attr("data-width")){d.attr("data-width",d.innerWidth())}if(!e.attr("data-width")&&e.innerWidth()!=0&&e.innerWidth()!=-1){e.attr("data-width",e.innerWidth())}c=parseInt(d.attr("data-width"));f=parseInt(e.attr("data-width"));$(".file_nav_view>.nav_group").not(".mount_disk_list").each(function(){a+=$(this).innerWidth()});a+=$(".menu-header-foot").innerWidth();if(b-a<(c+5)){$(".nav_group.mount_disk_list").addClass("thezoom").find(".disk_title_group_btn").removeClass("hide")}else{$(".nav_group.mount_disk_list,.nav_group.multi").removeClass("thezoom")}if(this.area[0]<1360){indexs=Math.ceil(((1360-this.area[0])/68));$(".batch_group_list>.nav_btn_group").each(function(g){if(g>=$(".batch_group_list>.nav_btn_group").length-(indexs+2)){$(this).hide()}else{$(this).show()}});$(".batch_group_list>.nav_btn_group:last-child").removeClass("hide").show()}else{$(".batch_group_list>.nav_btn_group").css("display","inline-block");$(".batch_group_list>.nav_btn_group:last-child").addClass("hide")}},set_file_forward:function(){var b=this,a=$(".forward_path span");if(b.file_operating.length==1){a.addClass("active")}else{if(b.file_pointer==b.file_operating.length-1){a.eq(0).removeClass("active");a.eq(1).addClass("active")}else{if(b.file_pointer==0){a.eq(0).addClass("active");a.eq(1).removeClass("active")}else{a.removeClass("active")}}}},set_file_view:function(){var b=$(".file_list_content"),a=this.area[1]-$(".file_table_view")[0].offsetTop-170;$(".file_bodys").height(this.area[1]-100);if((this.file_list.length*50)>a){b.attr("data-height",b.data("height")||b.height()).css({overflow:"hidden","overflow-y":"auto",height:a+"px"});$(".file_shadow_bottom").css("opacity",1)}else{b.css({overflow:"hidden","overflow-y":"auto",height:a+"px"});$(".file_shadow_top,.file_shadow_bottom").css("opacity",0)}},open_share_view:function(){var a=this;layer.open({type:1,shift:5,closeBtn:2,area:["850px","580px"],title:"分享列表",content:'
            分享名称文件地址过期时间操作
            ',success:function(){a.render_share_list();$(".download_url_list").on("click",".info_down",function(){var b=$(this).attr("data-index");a.file_share_view(a.file_share_list[b],"list")});$(".download_table .download_url_page").on("click","a",function(c){var b=$(this).attr("href").match(/p=([0-9]+)$/)[1];a.render_share_list({p:b});c.stopPropagation();c.preventDefault()})}})},render_share_list:function(c){var b=this,a="";if(typeof c=="undefined"){c={p:1}}bt_tools.send("files/get_download_url_list",c,function(d){b.file_share_list=d.data;if(d.data.length>0){$.each(d.data,function(e,f){a+=''+f.ps+''+f.filename+""+bt.format_data(f.expire)+'详情 | 关闭'})}else{a='暂无分享数据'}$(".download_url_list").html(a);$(".download_url_page").html(d.page);$(".download_table").on("click",".del_down",function(){var f=$(this).attr("data-id"),e=$(this).attr("data-ps");b.remove_download_url({id:f,fileName:e},function(g){if(g.status){b.render_share_list(c)}layer.msg(g.msg,{icon:g.status?1:2})})})},"获取分享列表")},remove_check_file:function(d,b,c){var a=d.length;while(a--){if(d[a][b]==c){d.splice(a,1)}}},open_download_view:function(){var a=this;a.reader_form_line({url:"DownloadFile",beforeSend:function(b){return{url:b.url,path:b.path,filename:b.filename}},overall:{width:"310px"},data:[{label:"URL地址:",name:"url",placeholder:"URL地址",value:"http://",eventType:["input","focus"],input:function(){var c=$(this).val(),b=c.split("/");$('[name="filename"]').val(b[b.length-1])}},{label:"下载到:",name:"path",placeholder:"下载到",value:a.file_path},{label:"文件名:",name:"filename",placeholder:"保存文件名",value:"",eventType:"enter",enter:function(){$(".download_file_view .layui-layer-btn0").click()}}]},function(c,b){var d=bt.open({type:1,title:"下载文件",area:"500px",shadeClose:false,skin:"download_file_view",content:b[0].outerHTML,btn:["确认","关闭"],success:function(){c.setEvent()},yes:function(f,e){var g=c.getVal();if(!bt.check_url(g.url)){layer.msg("请输入有效的url地址..",{icon:2});return false}c.submitForm(function(h){a.render_present_task_list();layer.msg(h.msg,{icon:h.status?1:2});d.close()})}})})},set_style_width:function(d,c){var b=bt.get_cookie("formHeader")||$("#file_list_info").html(),a="",f=new RegExp("\\.file_"+d+"\\s?\\{width\\s?\\:\\s?(\\w+)\\s\\!important;\\}","g"),e={name:150,type:80,size:80,mtime:150,accept:80,user:80,ps:150};a=b.replace(f,function(h,g,j,i){return".file_"+d+"{width:"+(c<80?e[d]+"px":c+"px")+" !important;}"});$("#file_list_info").html(a)},set_file_table_width:function(){var f=this,i=$(".file_table_view")[0].offsetWidth,h=0,a=0,j=0,e="",c=0,d=45,g="",b={};$.each(this.file_header,function(k,l){if(l=="auto"){h++;b[k]=0}else{a+=l;e+="."+k+"{width:"+(k!="file_operation"?l:l-16)+"px !important;}"}});if(this.is_mobile){$(".file_operation.file_th").attr("style","margin-right:-10px !important;")}if((this.file_list.length*d)>$(".file_list_content").height()){b.file_tr=i-(this.is_mobile?0:this.scroll_width);i=i;g+=".file_td.file_operation{width:"+(this.file_header.file_operation-(this.is_mobile?0:this.scroll_width)-10)+"px !important;}";g+=".file_th.file_operation{padding-right:"+(10+(this.is_mobile?0:this.scroll_width))+"px !important}"}else{i=i;b.file_tr=i;if(this.is_mobile){g+=".file_td.file_operation{width:"+(this.file_header.file_operation-20)+"px !important;}"}}b.file_list_header=i;j=i-a;c=j/h;$.each(b,function(k,l){e+="."+k+"{width:"+(l==0?c:l)+"px !important;}"});$("#file_list_info").html(e+g)},render_path_list:function(h){var e=this,d='
            ',b="",g=this.file_path.split("/").splice(1),f=this.file_path.split("/")[0];if(bt.os==="Windows"){if(g.length==0){g=[]}g.unshift('本地磁盘('+f+")")}else{if(this.file_path=="/"){g=[]}g.unshift("根目录")}for(var c=0;c '+g[c]+' '}$(".path_input").val("").attr("data-path",this.file_path);var a=$(".file_path_input .file_dir_view");a.html(d);a.attr("data-width",a.width());e.set_dir_view_resize.delay(e,100)},render_path_down_list:function(b,e,f){var c=this,a="",d=$(b).parent().next().find(".file_dir").attr("title");this.get_dir_list({path:e},function(g){$.each(c.data_reconstruction(g.DIR),function(h,j){var i=(e!="/"?e:"")+"/"+j.filename;a+='
          • '+j.filename+"
          • "});$(b).html(a)})},reader_file_list:function(d,g){var c=this,a="",e="",b=bt.get_cookie("rank"),f=bt.get_cookie("record_paste_type");if(f!="null"&&f!=undefined){$(".file_nav_view .file_all_paste").removeClass("hide")}else{$(".file_nav_view .file_all_paste").addClass("hide")}$(".file_table_view").removeClass(".list_view,.icon_view").addClass(b=="list"?"list_view":"icon_view");$(".cut_view_model:nth-child("+(b=="list"?"2":"1")+")").addClass("active").siblings().removeClass("active");this.file_images_list=[];this.get_dir_list(d,function(h){if(h.status===false&&h.msg.indexOf("指定目录不存在!")>-1){return c.reader_file_list({path:"/www"})}c.file_path=c.path_check(h.PATH);c.file_list=$.merge(c.data_reconstruction(h.DIR,"DIR"),c.data_reconstruction(h.FILES));c.is_recycle=h.FILE_RECYCLE;c.file_store_list=h.STORE;bt.set_cookie("Path",c.path_check(h.PATH));c.reader_file_list_content(c.file_list,function(n){$(".path_input").attr("data-path",c.file_path);$(".file_nav_view .multi").addClass("hide");$(".selection_right_menu").removeAttr("style");var m=["100","200","500","1000","2000"];for(var j=0;j"+k+""}var l=$(h.PAGE);l.append('每页");$(".filePage").html('
            共'+n.is_dir_num+"个目录,"+(c.file_list.length-n.is_dir_num)+'个文件,文件大小:计算
            '+l[0].outerHTML);if(d.is_operating){d.is_operating=false}if(d.is_operating&&c.file_operating[c.file_pointer]!=h.PATH){e=c.file_operating[c.file_pointer+1];if(typeof e!="undefined"&&e!=h.PATH){c.file_operating.splice(c.file_pointer+1)}c.file_operating.push(h.PATH);c.file_pointer=c.file_operating.length-1}c.render_path_list();c.set_file_forward();c.render_favorites_list();c.set_file_view();c.set_file_table_width();if(g){g(h)}})})},data_reconstruction:function(c,a,f){var b=this,e=[],d=[["/etc","PS: 系统主要配置文件目录"],["/home","PS: 用户主目录"],["/tmp","PS: 公共的临时文件存储点"],["/root","PS: 系统管理员的主目录"],["/home","PS: 用户主目录"],["/usr","PS: 系统应用程序目录"],["/boot","PS: 系统启动核心目录"],["/lib","PS: 系统资源文件类库目录"],["/mnt","PS: 存放临时的映射文件系统"],["/www","PS: 宝塔面板程序目录"],["/bin","PS: 存放二进制可执行文件目录"],["/dev","PS: 存放设备文件目录"],["/www/wwwlogs","PS: 默认网站日志目录"],["/www/server","PS: 宝塔软件安装目录"],["/www/wwwlogs","PS: 网站日志目录"],["/www/Recycle_bin","PS: 回收站目录,勿动"],["/www/server/panel","PS: 宝塔主程序目录,勿动"],["/www/server/panel/plugin","PS: 宝塔插件安装目录"],["/www/server/panel/BTPanel","PS: 宝塔面板前端文件"],["/www/server/panel/BTPanel/static","PS: 宝塔面板前端静态文件"],["/www/server/panel/BTPanel/templates","PS: 宝塔面板前端模板文件"],[bt.get_cookie("backup_path"),"PS: 默认备份目录"],[bt.get_cookie("sites_path"),"PS: 默认建站目录"]];if(c.length<1){return[]}$.each(c,function(g,l){var j=l.split(";"),m="",n=j[0].split("."),k=n[n.length-1];switch(j[0]){case".user.ini":m="PS: PHP用户配置文件(防跨站)!";break;case".htaccess":m="PS: Apache用户配置文件(伪静态)";break;case"swap":m="PS: 宝塔默认设置的SWAP交换分区文件";break}if(j[0].indexOf(".upload.tmp")!=-1){m="PS: 宝塔文件上传临时文件,重新上传从断点续传,可删除"}for(var h=0;h0){$.each(this.file_store_list,function(b,c){a+='
          • '+c.name+"
          • "});a+='
          • 管理
          • '}else{a='
          • (空)
          • '}$(".favorites_file_path .nav_down_list").html(a)},set_favorites_manage:function(){var a=this;layer.open({type:1,title:"管理收藏夹",area:["850px","580px"],closeBtn:2,shift:5,shadeClose:false,content:"
            路径操作
            ",success:function(b){a.render_favorites_type_list();setTimeout(function(){$(b).css("top",($(window).height()-$(b).height())/2)},50)},cancel:function(){a.reader_file_list({path:a.file_path})}})},render_favorites_type_list:function(){var a="";this.$http("get_files_store",function(b){if(b.length>0){$.each(b,function(d,c){a+=''+c.path+'删除"})}else{a='暂无收藏'}$(".favorites_body").html(a);if(jQuery.prototype.fixedThead){$(".stroe_tab_list .divtable").fixedThead({resize:false})}else{$(".stroe_tab_list .divtable").css({overflow:"auto"})}})},load_favorites_index_list:function(){var a=this;this.$http("get_files_store",function(b){a.file_store_list=b;a.render_favorites_list()})},del_favorites:function(b){var a=this;layer.confirm("是否确定删除路径【"+b+"】?",{title:"删除收藏夹",closeBtn:2,icon:3},function(c){a.$http("del_files_store",{path:b},function(d){if(d.status){a.render_favorites_type_list()}layer.msg(d.msg,{icon:d.status?1:2})})})},reader_file_list_content:function(e,f){var a="",d=this,c=0,b=0;$.each(e,function(i,l){var j=l.filename,h=bt.get_random(10),m=(d.file_path+"/"+l.filename).replace("//","/"),k=d.determine_file_type(l.ext,"compress"),g=(function(){var n="打开";switch(d.determine_file_type(l.ext)){case"images":n="预览";break;case"video":n="播放";break;default:if(d.determine_file_type(l.ext)=="compress"){n=""}else{n="编辑"}break}l.type=="dir"?n="打开":"";return n}(l));d.file_list[i]["only_id"]=h;a+='
            "+l.filename+l.soft_link+""+(l.caret?'':"")+(l.down_id!=0?'':"")+'
            '+(l.type=="dir"?"文件夹":d.ext_type_tips(l.ext))+'
            '+l.user+" / "+l.root_level+'
            '+(l.type=="dir"?'计算':bt.format_size(l.size))+'
            '+bt.format_data(l.mtime)+'
            '+(l.is_os_ps?l.ps:'')+'
            ';if(l.type=="dir"){c++}l.path=m;l.open_type=d.determine_file_type(l.ext);if(l.open_type=="images"){l.images_id=b;d.file_images_list.push(l.path);b++}});$(".file_list_content").html(a);if(f){f({is_dir_num:c})}d.clear_table_active()},render_file_disk_list:function(){var c=this,a="",b="";c.get_disk_list(function(d){$.each(d,function(e,f){a+='";b+='
          • '+(f.path=="/"?"根目录":f.path)+" ("+f.size[2]+")
          • "});$(".mount_disk_list").html('
            磁盘分区
            '+a+"
            ");c.set_menu_line_view_resize()})},render_file_groud_menu:function(h,b){var f=this,g=$(b).data("index"),a="打开",d=f.file_list[g],j=["zip","rar","gz","war","tgz","bz2"],i=0,c={open:a,split_0:true,download:"下载",share:"分享目录/文件",cancel_share:"取消分享",favorites:"收藏目录/文件",cancel_favorites:"取消收藏",split_1:true,dir_kill:"目录查杀",authority:"权限",split_2:true,copy:"复制",shear:"剪切",rename:"重命名",del:"删除",split_3:true,compress:"创建压缩",unzip:"解压",open_find_dir:"打开文件所在目录",split_4:true,property:"属性"};switch(f.determine_file_type(d.ext)){case"images":a="预览";break;case"video":a="播放";break;default:a="编辑";break}c.open=(d.type=="dir"?"打开":a);if(d.type==="dir"){delete c.download}if(d.open_type=="compress"){delete c.open}if(d.down_id!=0){delete c.share}else{delete c.cancel_share;c.share=(d.type=="dir"?"分享目录":"分享文件")}if(d.caret!==false){delete c.favorites}else{delete c.cancel_favorites;c.favorites=(d.type=="dir"?"收藏目录":"收藏文件")}if(d.ext=="php"){c.dir_kill="文件查杀"}if(d.ext!="php"&&d.type!="dir"){delete c.dir_kill}var e=0;$.each(j,function(k,l){if(l==d.ext){e++}});if(e==0){delete c.unzip}if(!d.is_search){delete c.open_find_dir}else{c.open_find_dir=(d.type=="dir"?"打开该目录":"打开文件所在目录")}f.file_selection_operating=c;f.reader_menu_list({el:$(".selection_right_menu"),ev:h,data:d,list:c})},render_file_all_menu:function(e,c){var d=this,b={refresh:"刷新",split_0:true,upload:"上传",create:["新建文件夹/文件",{create_dir:"新建文件夹",create_files:"新建文件",soft_link:"软链接文件"}],web_shell:"终端",split_1:true,paste:"粘贴"},a=0,f=bt.get_cookie("record_paste_type");if(f=="null"||f==undefined){delete b.split_1;delete b.paste}d.reader_menu_list({el:$(".selection_right_menu"),ev:e,data:{},list:b})},render_files_multi_menu:function(e){var d=this,f=[["copy","复制"],["shear","剪切"],["authority","权限"],["compress","创建压缩"],["del","删除"]],c=$(".selection_right_menu").find("ul"),b=c.height(),a=c.width(),g=e.clientX-((this.area[0]-e.clientX)')}else{c.append($('
          • '+h[1]+"
          • ").append(i).on("click",{type:h[0],data:d.file_table_arry},function(k){$(".selection_right_menu").removeAttr("style");d.batch_file_manage(k.data.type);k.stopPropagation();k.preventDefault()}))}});$(".selection_right_menu").css({left:g,top:e.clientY-((this.area[1]-e.clientY)')}else{if(Array.isArray(l)){i=$('
              ');j=i.find(".set_group");$.each(l[1],function(n,m){j.append($('
            • '+m+"
            • ").on("click",{type:n,data:b.data},function(o){f.file_groud_event($.extend(o.data.data,{open:o.data.type,index:parseInt($(b.ev.currentTarget).data("index")),element:b.ev.currentTarget,type_tips:b.data.type=="dir"?"文件夹":"文件"}));b.el.removeAttr("style");o.stopPropagation();o.preventDefault()}))})}e.append($('
            • '+(Array.isArray(l)?l[0]:l)+"
            • ").append(i).on("click",{type:k,data:b.data},function(m){f.file_groud_event($.extend(m.data.data,{open:m.data.type,index:parseInt($(b.ev.currentTarget).data("index")),element:b.ev.currentTarget,type_tips:b.data.type=="dir"?"文件夹":"文件"}));if(k!="compress"&&k!="create"){b.el.removeAttr("style")}m.stopPropagation();m.preventDefault()}))}});d=e.innerHeight();g=b.ev.clientY-((this.area[1]-b.ev.clientY)当前未开启回收站,批量删除后将无法恢复,是否继续删除?',function(){a.$http("SetBatchData",b,function(c){if(c.status){a.reader_file_list({path:a.file_path})}layer.msg(c.msg,{icon:c.status?1:2})})})}},batch_file_paste:function(){var a=this,b=bt.get_cookie("record_paste_type");this.check_exists_files_req({dfile:this.file_path},function(c){if(c.length>0){var d="";for(var e=0;e'+c[e].filename+""+ToSize(c[e].size)+""+getLocalTime(c[e].mtime)+""}var f='
              '+d+"
              文件名大小最后修改时间
              ";SafeMessage("即将覆盖以下文件",f,function(){a.$http("BatchPaste",{type:b,path:a.file_path},function(g){if(g.status){bt.set_cookie("record_paste_type",null);a.reader_file_list({path:a.file_path})}layer.msg(g.msg,{icon:g.status?1:2})})})}else{a.$http("BatchPaste",{type:b,path:a.file_path},function(g){if(g.status){bt.set_cookie("record_paste_type",null);a.reader_file_list({path:a.file_path})}layer.msg(g.msg,{icon:g.status?1:2})})}})},recycle_bin_view:function(){var a=this;layer.open({title:lan.files.recycle_bin_title,type:1,skin:"recycle_view",area:["1100px","672px"],closeBtn:2,content:'
              '+lan.files.recycle_bin_on+'
              '+lan.files.recycle_bin_on_db+'
              '+lan.files.recycle_bin_ps+'

              '+lan.files.recycle_bin_type1+'

              '+lan.files.recycle_bin_type2+'

              '+lan.files.recycle_bin_type3+'

              '+lan.files.recycle_bin_type4+'

              '+lan.files.recycle_bin_type5+'

              '+lan.files.recycle_bin_type6+'

              ',success:function(){if(window.location.href.indexOf("database")!=-1){$(".re-con-menu p:last-child").addClass("on").siblings().removeClass("on");$(".re-con-menu p:eq(5)").click()}else{$(".re-con-menu p:eq(0)").click()}var b=a.render_recycle_list();$(".re-con-menu").on("click","p",function(){var c=$(this).data("type");$(this).addClass("on").siblings().removeClass("on");b.$refresh_table_list(true)})}})},render_recycle_list:function(){var a=this;$("#recycle_table").empty();var b=bt_tools.table({el:"#recycle_table",url:"/files?action=Get_Recycle_bin",height:480,dataFilter:function(e){var k=[];switch($(".re-con-menu p.on").index()){case 0:for(var d=0;d-1){h.dname=h.dname.replace("BTDB_","");h.name=h.name.replace("BTDB_","");k.push($.extend(h,{type:"files"}))}else{if(f=="images"){k.push($.extend(h,{type:f}))}else{k.push($.extend(h,{type:"files"}))}}}break;case 1:for(var d=0;d-1){h.dname=h.dname.replace("BTDB_","");h.name=h.name.replace("BTDB_","");console.log();k.push($.extend(h,{type:"files"}))}}break}$("#Set_Recycle_bin").attr("checked",e.status);$("#Set_Recycle_bin_db").attr("checked",e.status_db);return{data:k}},column:[{type:"checkbox","class":"",width:18},{fid:"name",title:lan.files.recycle_bin_th1,width:155,template:function(c){return'
              '+c.name+"
              "}},{fid:"dname",title:lan.files.recycle_bin_th2,width:310,template:function(c){return''+c.dname+""}},{fid:"size",title:lan.files.recycle_bin_th3,width:70,template:function(c){return''+bt.format_size(c.size)+""}},{fid:"time",title:lan.files.recycle_bin_th4,width:120,template:function(d,c){return''+bt.format_data(d.time)+""}},{type:"group",align:"right",width:95,title:lan.files.recycle_bin_th5,group:[{title:lan.files.recycle_bin_re,event:function(g,c,f,d,e){bt_file.ReRecycleBin(g.rname,function(){e.$delete_table_row(c)})}},{title:lan.files.recycle_bin_del,event:function(g,c,f,d,e){bt_file.DelRecycleBin(g.rname,function(){e.$delete_table_row(c)})}}]}],tootls:[{type:"batch",positon:["left","bottom"],placeholder:"请选择批量操作",buttonValue:"批量操作",disabledSelectValue:"请选择需要批量操作的端口!",selectList:[{title:"恢复",url:"/files?action=Re_Recycle_bin",load:true,param:function(c){return{path:c.rname}},callback:function(c){bt.confirm({title:"批量恢复文件",msg:"是否批量恢复选中的文件,是否继续?",icon:0},function(d){layer.close(d);c.start_batch({},function(h){var f="";for(var e=0;e"+g.name+'
              '+(g.request.status?"恢复成功":"恢复失败")+"
              "}b.$batch_success_table({title:"批量恢复文件",th:"文件名称",html:f});b.$refresh_table_list(true)})})}},{title:"永久删除文件",url:"/files?action=Del_Recycle_bin",load:true,param:function(c){return{path:c.rname}},callback:function(c){bt.confirm({title:"批量删除文件",msg:"是否批量删除选中的文件,文件将彻底删除,不可恢复,是否继续?",icon:0},function(d){layer.close(d);c.start_batch({},function(h){var f="";for(var e=0;e"+g.name+'
              '+(g.request.status?"删除成功":"删除失败")+"
              "}b.$batch_success_table({title:"批量删除文件",th:"文件名称",html:f});b.$refresh_table_list(true)})})}}]}]});bt_tools.$fixed_table_thead("#recycle_table .divtable");return b},Set_Recycle_bin:function(a){var d=layer.msg(lan["public"].the,{icon:16,time:0,shade:[0.3,"#000"]});var b=this,c={};if(a==1){c={db:a}}$.post("/files?action=Recycle_bin",c,function(e){layer.close(d);if(e.status){if(a==undefined){b.is_recycle=$("#Set_Recycle_bin").prop("checked")}}layer.msg(e.msg,{icon:e.status?1:5})})},ReRecycleBin:function(a,b){layer.confirm(lan.files.recycle_bin_re_msg,{title:lan.files.recycle_bin_re_title,closeBtn:2,icon:3},function(){var c=layer.msg(lan.files.recycle_bin_re_the,{icon:16,time:0,shade:[0.3,"#000"]});$.post("/files?action=Re_Recycle_bin","path="+encodeURIComponent(a),function(d){layer.close(c);layer.msg(d.msg,{icon:d.status?1:5});if(b){b(d)}})})},DelRecycleBin:function(a,b){layer.confirm(lan.files.recycle_bin_del_msg,{title:lan.files.recycle_bin_del_title,closeBtn:2,icon:3},function(){var c=layer.msg(lan.files.recycle_bin_del_the,{icon:16,time:0,shade:[0.3,"#000"]});$.post("/files?action=Del_Recycle_bin","path="+encodeURIComponent(a),function(d){layer.close(c);layer.msg(d.msg,{icon:d.status?1:5});if(b){b(d)}})})},CloseRecycleBin:function(){var a=this;layer.confirm(lan.files.recycle_bin_close_msg,{title:lan.files.recycle_bin_close,closeBtn:2,icon:3},function(){var b=layer.msg("
              "+lan.files.recycle_bin_close_the+"
              ",{icon:16,time:0,shade:[0.3,"#000"]});setTimeout(function(){getSpeed(".myspeed")},1000);$.post("/files?action=Close_Recycle_bin","",function(c){layer.close(b);layer.msg(c.msg,{icon:c.status?1:5});a.render_recycle_list();$("#RecycleBody").html("")})})},open_property_view:function(a){console.log(a);var b=this;b.$http("get_file_attribute",{filename:a.path},function(c){layer.open({type:1,closeBtn:2,title:"[ "+a.filename+" ] - "+(a.is_dir?"文件夹":"文件")+"属性",area:["580px","500px"],shadeClose:false,content:'
              常规 详细信息 历史版本
              类型:
              '+((c.is_dir||c.is_link)?c.st_type:b.ext_type_tips(c.st_type))+'
              文件路径:
              '+c.path+'
              大小:
              '+bt.format_size(c.st_size)+" ("+(b.font_thousandth(c.st_size)+" 字节")+')
              权限:
              '+c.mode+'
              所属组:
              '+c.group+'
              所属用户:
              '+c.user+'
              访问时间:
              '+bt.format_data(c.st_atime)+'
              修改时间:
              '+bt.format_data(c.st_mtime)+'
              属性
              修改时间
              文件大小
              MD5
              操作
              ',success:function(j,h){$(".bt-property-setting .tab-nav span").click(function(){var i=$(this).index();$(this).addClass("on").siblings().removeClass("on");$(".property-box:eq("+i+")").addClass("active").siblings().removeClass("active")});$(".history_box_view").on("click",".open_history_file",function(){var i=$(this).attr("data-time");openEditorView(0,a.path);setTimeout(function(){aceEditor.openHistoryEditorView({filename:a.path,history:i},function(){layer.close(h);$(".ace_conter_tips").show();$(".ace_conter_tips .tips").html("只读文件,文件为"+_item.path+",历史版本 [ "+bt.format_data(new Number(i))+' ]点击恢复当前历史版本')})},500)});$(".history_box_view").on("click",".recovery_file_historys",function(){aceEditor.event_ecovery_file(this)});var d={filename:["文件名",a.filename],type:["类型",(c.is_dir||c.is_link)?c.st_type:b.ext_type_tips(c.st_type)],path:"文件路径",st_size:["文件大小",bt.format_size(c.st_size)+" ("+(b.font_thousandth(c.st_size)+" 字节")+")"],st_atime:["访问时间",bt.format_data(c.st_atime)],st_mtime:["修改时间",bt.format_data(c.st_mtime)],st_ctime:["元数据修改时间",bt.format_data(c.st_ctime)],md5:"文件MD5",sha1:"文件sha1",user:"所属用户",group:"所属组",mode:"文件权限",lsattr:"特殊权限",st_uid:"用户id",st_gid:"用户组id",st_nlink:"inode的链接数",st_ino:"inode的节点号",st_mode:"inode保护模式",st_dev:"inode驻留设备"},g="",k="";for(var m in d){if(Object.hasOwnProperty.call(d,m)){var f=d[m],l=($.isArray(f)?f[1]:c[m]);g+='
              '+($.isArray(f)?f[0]:f)+'
              '+l+"
              "}}for(var e=0;e
              '+bt.format_data(n.st_mtime)+'
              '+bt.format_size(n.st_size)+"
              "+n.md5+'
              查看  |  恢复
              '}if(k===""){k+='
              当前文件无历史版本
              '}$(".details_list").html(g);$(".history_list ").html(k);b.fixed_table_thead(".details_box_view");b.fixed_table_thead(".history_box_view ")}})})},fixed_table_thead:function(a){$(a).scroll(function(){var b=this.scrollTop;this.querySelector("thead").style.transform="translateY("+b+"px)"})},font_thousandth:function(a){var b=String(a).split(".");b[0]=b[0].replace(new RegExp("(\\d)(?=(\\d{3})+$)","ig"),"$1,");return b.join(".")},open_images_preview:function(e){var d=this,a=$('
              '+e.filename+'
              '),c={natural_width:0,natural_height:0,init_width:0,init_height:0,preview_width:0,preview_height:0,current_width:0,current_height:0,current_left:0,current_top:0,rotate:0,scale:1,images_mouse:false};if($(".preview_images_mask").length>0){$("#preview_images").attr("src","/download?filename="+e.path);return false}$("body").css("overflow","hidden").append(a);c.preview_width=a[0].clientWidth;c.preview_height=a[0].clientHeight;$(".preview_body img").load(function(){var f=$(this)[0];if(!$(this).attr("data-index")){$(this).attr("data-index",e.images_id)}c.natural_width=f.naturalWidth;c.natural_height=f.naturalHeight;b(false)});$(".preview_images_mask .preview_head").on("mousedown",function(i){i=i||window.event;var h=$(this).parent();$("body").addClass("select");$(this).onselectstart=$(this).ondrag=function(){return false};if($(i.target).hasClass("preview_close")){return}var g=i.clientX-h.offset().left;var f=i.clientY-h.offset().top;$(document).on("mousemove",function(l){l=l||window.event;var k=l.clientX-g;var j=l.clientY-f;if(k<0){k=0}else{if(k>window.innerWidth-h.width()){k=window.innerWidth-h.width()}}if(j<0){j=0}else{if(j>window.innerHeight-h.height()){j=window.innerHeight-h.height()}}h.css({left:k,top:j,margin:0})}).on("mouseup",function(){$(this).unbind("mousemove mouseup")})});$(".preview_images_mask #preview_images").on("mousedown",function(j){j=j||window.event;$(this).onselectstart=$(this).ondrag=function(){return false};var f=$(this);var i=$(".preview_images_mask").offset();var h=j.clientX-i.left;var g=j.clientY-i.top;$(".preview_images_mask").on("mousemove",function(p){p=p||window.event;var o=p.clientX-i.left-h,n=p.clientY-i.top-g,l=Math.abs(c.rotate/90),q=(l%2==0?c.preview_width:c.preview_height),s=(l%2==0?c.preview_height:c.preview_width),k,r;if(c.current_width>q){var t=q-c.current_width;k=c.current_left+o;if(k>0){k=0}else{if(ks){var m=s-c.current_height;r=c.current_top+n;if(r>0){r=0}else{if(rs&&c.current_top<=0){if((c.current_height-s)<=c.current_top){c.current_top-=n}}f.css({left:c.current_left,top:c.current_top})}).on("mouseup",function(){$(this).unbind("mousemove mouseup")}).on("dragstart",function(){j.preventDefault()})}).on("dragstart",function(){return false});$(".preview_close").click(function(f){$(".preview_images_mask").remove()});$(".preview_toolbar a").click(function(){var g=$(this).index(),f=$("#preview_images");switch(g){case 0:case 1:c.rotate=g?(c.rotate+90):(c.rotate-90);b();break;case 2:case 3:if(c.scale==3&&g==2||c.scale==0.2&&g==3){layer.msg((c.scale>=1?"图像放大,已达到最大尺寸。":"图像缩小,已达到最小尺寸。"));return false}c.scale=(g==2?Math.round((c.scale+0.4)*10):Math.round((c.scale-0.4)*10))/10;b();break;case 4:var h=c.rotate%360;if(h>=180){c.rotate+=(360-h)}else{c.rotate-=h}c.scale=1;b();break}});$(".preview_full,.preview_small").click(function(){if($(this).hasClass("preview_full")){$(this).addClass("hidden").prev().removeClass("hidden");c.preview_width=d.area[0];c.preview_height=d.area[1];a.css({width:d.area[0],height:d.area[1],top:0,left:0,margin:0}).data("type","full");b()}else{$(this).addClass("hidden").next().removeClass("hidden");$(".preview_images_mask").removeAttr("style");c.preview_width=750;c.preview_height=650;b()}});$(".preview_cut_view a").click(function(){var h="",f=$("#preview_images"),g=parseInt(f.attr("data-index"));if(!$(this).index()){g=g===0?(d.file_images_list.length-1):g-1;h=d.file_images_list[g]}else{g=(g==(d.file_images_list.length-1))?0:(g+1);h=d.file_images_list[g]}f.attr("data-index",g).attr("src","/download?filename="+h);$(".preview_title").html(d.get_path_filename(h))});function b(k){var h=Math.abs(c.rotate/90),g=(h%2==0?c.preview_width:c.preview_height),j=(h%2==0?c.preview_height:c.preview_width),i=$("#preview_images"),f={};c.init_width=c.natural_width;c.init_height=c.natural_height;if(c.init_width>g){c.init_width=g;c.init_height=parseFloat(((g/c.natural_width)*c.init_height).toFixed(2))}if(c.init_height>j){c.init_width=parseFloat(((j/c.natural_height)*c.init_width).toFixed(2));c.init_height=j}c.current_width=parseFloat(c.init_width*c.scale);c.current_height=parseFloat(c.init_height*c.scale);c.current_left=parseFloat(((c.preview_width-c.current_width)/2).toFixed(2));c.current_top=parseFloat(((c.preview_height-c.current_height)/2).toFixed(2));f={width:c.current_width,height:c.current_height,top:c.current_top,left:c.current_left,display:"inline",transform:"rotate("+c.rotate+"deg)",opacity:1,transition:"all 100ms"};if(k===false){delete f.transition}i.css(f)}},open_video_play:function(b){var e=b.path,d="/download?filename="+b.path,a=b.path.split("/"),c=a.slice(0,a.length-1).join("/");layer.open({type:1,closeBtn:2,title:'正在播放['+a[a.length-1]+"]",area:["890px","402px"],shadeClose:false,skin:"movie_pay",content:'
              ',success:function(){$.post("/files?action=get_videos",{path:c},function(k){var j='',g=0;for(var h=0;h"}j+="
              文件名大小
              '+k[h].name+''+ToSize(k[h].size)+"
              ";$(".video-list").html(j).scrollTop(g*34)})}})},play_file:function(d,b){if($("#btvideo video").attr("data-filename")==b){return false}var e="/download?filename="+b+"&play=true";var a='';$("#btvideo").html(a);var c=b.split("/");$(".btvideo-title").html(c[c.length-1]);$(".video-avt").removeClass("video-avt");$(d).parents("tr").addClass("video-avt")},copy_file_or_dir:function(a){bt.set_cookie("record_paste",a.path);bt.set_cookie("record_paste_type","copy");$(".file_all_paste").removeClass("hide");layer.msg("复制成功,请点击粘贴按钮,或Ctrl+V粘贴")},cut_file_or_dir:function(a){bt.set_cookie("record_paste",a.path);bt.set_cookie("record_paste_type","cut");$(".file_all_paste").removeClass("hide");layer.msg("剪切成功,请点击粘贴按钮,或Ctrl+V粘贴")},paste_file_or_dir:function(){var c=this,d=bt.get_cookie("record_paste_type"),a=bt.get_cookie("record_paste"),b="";if(a!="null"&&a!=undefined){b=a.split("/").pop()}if(c.file_path.indexOf(a)>-1){layer.msg("文件夹禁止粘贴到项目本身!",{icon:0});return false}if(d!="null"&&d!=undefined){switch(d){case"cut":case"copy":this.check_exists_files_req({dfile:this.file_path,filename:b},function(e){if(e.length>0){var f="";for(var g=0;g'+e[g].filename+""+ToSize(e[g].size)+""+getLocalTime(e[g].mtime)+""}var h='
              '+f+"
              文件名大小最后修改时间
              ";SafeMessage("即将覆盖以下文件",h,function(){c.config_paste_to(a,b)})}else{c.config_paste_to(a,b)}});break;case"1":case"2":c.batch_file_paste();break}}},config_paste_to:function(d,b){var c=this,a=bt.get_cookie("record_paste_type");this.$http(a=="copy"?"CopyFile":"MvFile",{sfile:d,dfile:(this.file_path+"/"+b)},function(e){if(e.status){bt.set_cookie("record_paste",null);bt.set_cookie("record_paste_type",null);c.reader_file_list({path:c.file_path})}layer.msg(e.msg,{icon:e.status?1:2})})},rename_file_or_dir:function(b){var a=this;a.is_editor=true;$(".file_list_content .file_tr:nth-child("+(b.index+1)+")").addClass("editr_tr").find(".file_title").empty().append($((bt.get_cookie("rank")=="icon"?'":'')));if(bt.get_cookie("rank")=="icon"){$("textarea[name=rename_file_input]").css({height:$("textarea[name=rename_file_input]")[0].scrollHeight})}$((bt.get_cookie("rank")=="icon"?"textarea":"input")+"[name=rename_file_input]").on("input",function(){if(bt.get_cookie("rank")=="icon"){this.style.height="auto";this.style.height=this.scrollHeight+"px"}if(b.type=="file"){var d=$(this).val().split("."),c=d[d.length-1];$(this).parent().prev().find(".file_icon").removeAttr("class").addClass("file_icon file_"+c)}}).keyup(function(c){if(c.keyCode==13){$(this).blur()}c.stopPropagation();c.preventDefault()}).blur(function(){var d=$(this).val().replace(/[\r\n]/g,""),c={sfile:b.path,dfile:a.path_resolve(a.file_path,d)};if(b.filename==d||d==""){$(".file_list_content .file_tr:nth-child("+(b.index+1)+")").removeClass("editr_tr").find(".file_title").empty().append($(""+b.filename+""));a.is_editor=false;return false}if(a.match_unqualified_string(d)){return layer.msg('名称不能含有 /\\:*?"<>|符号',{icon:2})}a.rename_file_req(c,function(e){a.reader_file_list({path:a.file_path},function(){layer.msg(e.msg,{icon:e.status?1:2})})});a.is_editor=false}).focus()},set_file_share:function(b){var a=this;this.loadY=bt.open({type:1,shift:5,closeBtn:2,area:"450px",title:"设置分享"+b.type_tips+"-["+b.filename+"]",btn:["生成外链","取消"],content:'
              分享名称
              有效期
              提取码
              ',yes:function(g,e){var f=$("[name=ps]").val(),c=$("[name=expire]:checked").val(),d=$("[name=password]").val();if(f===""){layer.msg("分享名称不能为空",{icon:2});return false}a.create_download_url({filename:b.path,ps:f,password:d,expire:c},function(h){if(!h.status){layer.msg(h.msg,{icon:h.status?1:2});return false}else{var i=h.msg;a.file_list[b.index]=$.extend(a.file_list[b.index],{down_id:i.id,down_info:i});a.loadY.close();a.info_file_share(b);a.reader_file_list_content(a.file_list)}})},success:function(d,c){$("#random_paw").click(function(){$(this).prev().val(bt.get_random(6))})}})},info_file_share:function(b){var a=this;if(typeof b.down_info=="undefined"){this.get_download_url_find({id:b.down_id},function(c){a.file_list[b.index]=$.extend(a.file_list[b.index],{down_info:c});a.file_share_view(a.file_list[b.index],"fonticon")});return false}this.file_share_view(b,"fonticon")},file_share_view:function(a,c){var e=a;if(c=="fonticon"){e=a.down_info}var d=this,b=location.origin+"/down/"+e.token;this.loadY=bt.open({type:1,shift:5,closeBtn:2,area:"550px",title:"外链分享-["+e.filename+"]",content:'
              分享名称
              过期时间
              '+((e.expire>(new Date("2099-01-01 00:00:00").getTime())/1000)?'永久有效':bt.format_data(e.expire))+'
              ',success:function(i,g){var h=new ClipboardJS(".copy_url");var f=new ClipboardJS(".copy_paw");h.on("success",function(j){layer.msg("复制链接成功!",{icon:1});j.clearSelection()});f.on("success",function(j){layer.msg("复制链接及提取码成功!",{icon:1});j.clearSelection()});$(".layer_close").click(function(){layer.close(g)});$(".QR_code").click(function(){layer.closeAll("tips");layer.tips('
              ',".QR_code",{area:["150px","150px"],tips:[1,"#ececec"],time:0,shade:[0.05,"#000"],shadeClose:true,success:function(){jQuery("#QR_code").qrcode({render:"canvas",text:b,height:130,width:130})}})});$(".close_down").click(function(){d.remove_download_url({id:e.id,fileName:e.filename},function(j){d.loadY.close();if(c=="fonticon"){d.file_list[a.index].down_id=0;d.reader_file_list_content(d.file_list)}if(c=="list"){d.render_share_list()}layer.msg(j.msg,{icon:j.status?1:2})})})}})},del_file_or_dir:function(b){var a=this;if(a.is_recycle){bt.confirm({title:"删除"+b.type_tips+"[ "+b.filename+" ]",msg:"您确定要删除该"+b.type_tips+"[ "+b.path+" ]吗,删除后将移至回收站,是否继续操作?"},function(){a.del_file_req(b,function(c){a.reader_file_list({path:a.file_path});layer.msg(c.msg,{icon:c.status?1:2})})})}else{bt.show_confirm("删除"+b.type_tips+"[ "+b.filename+" ]",'当前未开启回收站,删除该'+(b.type=="dir"?"文件夹":"文件")+"后将无法恢复,是否继续删除?
              ",function(){a.del_file_req(b,function(c){a.reader_file_list({path:a.file_path});layer.msg(c.msg,{icon:c.status?1:2})})})}},cancel_file_favorites:function(c){var b=this,a=c.index;this.loadY=bt.confirm({title:"取消"+c.filename+"收藏",msg:"是否取消["+c.path+"]的收藏,是否继续?"},function(){b.$http("del_files_store",{path:c.path},function(d){if(d.status){b.file_list[a].caret=false;b.reader_file_list_content(b.file_list);b.load_favorites_index_list()}layer.msg(d.msg,{icon:d.status?1:2})})})},set_soft_link:function(b){var a=this;bt_tools.open({title:"创建软链接",area:"520px",content:{"class":"pd20",formLabelWidth:"110px",form:[{label:"文件夹或目录",group:{type:"text",name:"sfile",width:"280px",placeholder:"请选择需要创建的软链的文件夹和文件",icon:{type:"glyphicon-folder-open",event:function(c){},select:"all"},value:"",input:function(c){console.log(arguments)}}},{label:"",group:{type:"help",style:{"margin-top":"0"},"class":"none-list-style",list:["提示:请选择需要创建的软链的文件夹和文件"]}}]},yes:function(e,g,d){var c=e.sfile,f=c.split("/");e=$.extend(e,{dfile:a.file_path+"/"+f[f.length-1]});bt_tools.send("files/CreateLink",e,function(h){if(h.status){layer.close(g);bt.msg(h);a.reader_file_list()}},{tips:"创建软链接"})}})},set_file_authority:function(c,a){var b=this;b.get_file_authority({path:c.path},function(d){b.loadY=layer.open({type:1,closeBtn:2,title:lan.files.set_auth+"["+c.filename+"]",area:"400px",shadeClose:false,content:'
              '+lan.files.file_own+'

              '+lan.files.file_read+'

              '+lan.files.file_write+'

              '+lan.files.file_exec+"

              "+lan.files.file_group+'

              '+lan.files.file_read+'

              '+lan.files.file_write+'

              '+lan.files.file_exec+"

              "+lan.files.file_public+'

              '+lan.files.file_read+'

              '+lan.files.file_write+'

              '+lan.files.file_exec+'

              '+lan.files.file_menu_auth+", "+lan.files.file_own+'
              ",success:function(e,f){b.edit_access_authority();$("#access").keyup(function(){b.edit_access_authority()});$("input[type=checkbox]").change(function(){var i=["owner","group","public"];var h="";for(var j=0;j'})},render_present_task_list:function(){var a=this;this.get_task_req({status:-3},function(b){if(b.length==0){layer.close(a.file_present_task);a.file_present_task=null;a.reader_file_list({path:a.file_path,is_operating:false});return}var d="",c=false;$.each(b,function(e,f){if(f.status==-1){if(!a.file_present_task){a.get_present_task_view()}if(f.type=="1"){d+='
              • 正在下载: '+f.shell+''+f.log.pre+'%取消
                '+f.log.used+"/"+ToSize(f.log.total)+''+(f.log.speed==0?"正在连接..":f.log.speed)+'/s预计还要: '+f.log.time+"
              "}else{d+='
              '+f.name+": "+f.shell+'取消
              '+f.log+"
              "}}else{if(!c){d+='
              等待执行任务
                ';c=true}d+='
              • '+f.name+": "+f.shell+'X
              • '}});if(a.file_present_task){if(c){d+="
              "}$(".message-list").html(d)}setTimeout(function(){a.render_present_task_list()},1000)})},remove_present_task:function(b){var a=this;layer.confirm("是否取消上传当前列表的文件,若取消上传,已上传的文件,需用户手动删除,是否继续?",{title:"取消上传文件",icon:0},function(c){bt.send("remove_task","task/remove_task",{id:b},function(d){layer.msg(d.msg,{icon:1});layer.close(a.file_present_task);a.file_present_task=null});layer.close(c)})},edit_access_authority:function(){var a=$("#access").val();var c=["owner","group","public"];for(var e=0;ec.length){continue}if(b>7){$("#access").val(a.substr(0,a.length-1))}switch(b){case"1":$("#"+c[d]+"_x").prop("checked",true);break;case"2":$("#"+c[d]+"_w").prop("checked",true);break;case"3":$("#"+c[d]+"_x").prop("checked",true);$("#"+c[d]+"_w").prop("checked",true);break;case"4":$("#"+c[d]+"_r").prop("checked",true);break;case"5":$("#"+c[d]+"_r").prop("checked",true);$("#"+c[d]+"_x").prop("checked",true);break;case"6":$("#"+c[d]+"_r").prop("checked",true);$("#"+c[d]+"_w").prop("checked",true);break;case"7":$("#"+c[d]+"_r").prop("checked",true);$("#"+c[d]+"_w").prop("checked",true);$("#"+c[d]+"_x").prop("checked",true);break}}},get_file_authority:function(a,b){this.$http("GetFileAccess",{filename:a.path},function(c){if(b){b(c)}})},set_dir_kill:function(b){var a=this;if(b.ext=="php"){a.$http("file_webshell_check",{filename:b.path},function(c){layer.msg(c.msg,{icon:c.status?1:2})})}else{layer.confirm("目录查杀将包含子目录中的php文件,是否操作?",{title:"目录查杀["+b.filename+"]",closeBtn:2,icon:3},function(c){a.$http("dir_webshell_check",{path:b.path},function(d){layer.msg(d.msg,{icon:d.status?1:2})})})}},path_resolve:function(d,c){var b="",a="";if(!Array.isArray(c)){c=[c]}d.replace(/([\/|\/]*)$/,function(e){a=e;return"www"});$.each(c,function(e,f){b+="/"+f});return(d+b).replace("//","/")},get_ext_name:function(e){var c=e.split(".");var b=["folder","folder-unempty","sql","c","cpp","cs","flv","css","js","htm","html","java","log","mht","php","url","xml","ai","bmp","cdr","gif","ico","jpeg","jpg","JPG","png","psd","webp","ape","avi","mkv","mov","mp3","mp4","mpeg","mpg","rm","rmvb","swf","wav","webm","wma","wmv","rtf","docx","fdf","potm","pptx","txt","xlsb","xlsx","7z","cab","iso","rar","zip","gz","bt","file","apk","bookfolder","folder-empty","fromchromefolder","documentfolder","fromphonefolder","mix","musicfolder","picturefolder","videofolder","sefolder","access","mdb","accdb","fla","doc","docm","dotx","dotm","dot","pdf","ppt","pptm","pot","xls","csv","xlsm"];var d=c[c.length-1];for(var a=0;a')},yes:function(){var g=e.getVal();if(g.dfile==""){return layer.msg("请选择有效的地址",{icon:2})}e.submitForm(function(i,h){setTimeout(function(){a.reader_file_list({path:h.path})},1000);if(i==null||i==undefined){layer.msg(lan.files.zip_ok,{icon:1})}if(i.status){a.render_present_task_list()}layer.close(f)})}})})},unpack_file_to_path:function(d){var c=this,a="zip",b="";b=d.path.substring(0,d.path.lastIndexOf("/"));this.reader_form_line({url:"UnZip",overall:{width:"310px"},data:[{label:"文件名",name:"z_name",placeholder:"压缩文件名",value:d.path},{label:"解压到",name:"z_path",placeholder:"解压路径",value:b},{label:"编码",name:"z_code",type:"select",value:"UTF-8",list:[["UTF-8","UTF-8"],["gb18030","GBK"]]}],beforeSend:function(e){return{sfile:e.z_name,dfile:e.z_path,type:a,coding:e.z_code,password:e.z_password}}},function(f,e){var g=layer.open({type:1,title:"解压文件 [ "+d.filename+" ]",area:"480px",shadeClose:false,closeBtn:2,skin:"unpack_file_view",btn:["解压","关闭"],content:e[0].outerHTML,success:function(){if(d.ext=="gz"){a="tar"}if(a=="zip"){$(".unpack_file_view .line:nth-child(2)").append('
              解压密码
              ')}},yes:function(){var h=f.getVal();if(h.z_name==""){return layer.msg("请输入文件名路径",{icon:2})}if(h.z_path==""){return layer.msg("请输入解压地址",{icon:2})}f.submitForm(function(j,i){layer.close(g);setTimeout(function(){c.reader_file_list({path:i.path})},1000);if(j.status){c.render_present_task_list()}layer.msg(j.msg,{icon:j.status?1:2})})}})})},match_unqualified_string:function(a){var b=RegExp(/[(\*)(\|)(\\)(\:)(\")(\/)(\<)(\>)(\?)(\)]+/);return b.test(a)},reader_form_line:function(a,g){var e=this,d=bt.get_random(10),b=$('
              '),f=a,c=[],e=this;if(!Array.isArray(a)){f=a.data}$.each(f,function(k,m){var h=m.labelWidth||a.overall.labelWidth||null,i=bt.get_random(10),l=m.labelWidth||a.overall.width||null,j=$('
              "+(m.label||"")+'
              '),n=$((function(){switch(m.type){case"select":return"";break;case"text":default:return"';break}}(m)));if(m.eventType||m.event){if(!Array.isArray(m.eventType)){m.eventType=[m.eventType]}$.each(m.eventType,function(p,o){c.push({el:i,type:o||"click",event:m[o]||null});if(a.el){var q=$('[data-event="'+m.el+'"]');if(m[o]){if(o=="enter"){q.on("keyup",function(s){if(s.keyCode==13){m.event(s)}})}else{q.on(m||"click",m.event)}}else{if(o=="focus"){var r=q.val();if(r!=""){q.val("").focus().val(r)}}else{q[o]()}}}})}j.find(".info-r").append(n);b.append(j)});if(a.el){$(a.el).empty().append(b)}if(g){g({getVal:function(){return $("#"+d).serializeObject()},setEvent:function(){$.each(c,function(h,j){var i=$('[data-event="'+j.el+'"]');if(j.event===null){if(j.type=="focus"){var k=i.val();if(k!=""){i.val("").focus().val(k)}}else{i[j.type]()}}else{if(j.type=="enter"){i.on("keyup",function(l){if(l.keyCode==13){j.event(l)}})}else{i.on(j.type,j.event)}}})},submitForm:function(i){var h=this.getVal();if(a.beforeSend){h=a.beforeSend(h)}e.loadT=bt.load("提交表单内容");bt.send(a.url,("files/"+a.url),h,function(j){e.loadT.close();if(i){i(j,h)}})}},b)}},$http:function(c,b,e){var a=this,d="";if(typeof c=="string"){if(typeof b!="object"){e=b,b={}}if(!Array.isArray(a.method_list[c])){a.method_list[c]=["files",a.method_list[c]]}a.$http({method:c,tips:(a.method_list[c][1]?"正在"+a.method_list[c][1]+",请稍候...":false),module:a.method_list[c][0],data:b,msg:true},e)}else{if(typeof c.tips!="undefined"&&c.tips){d=bt.load(c.tips)}bt.send(c.method,(c.module||"files")+"/"+c.method,c.data||{},function(f){if(d!=""){d.close()}if(typeof f=="string"){f=JSON.parse(f)}if(f.status===false&&f.msg){bt.msg(f);return false}if(b){b(f)}})}}};bt_file.init();Function.prototype.delay=function(a,c,b){if(!Array.isArray(c)){b=c,c=[]}if(typeof b=="undefined"){b=0}setTimeout(this.apply(a,c),b);return this};jQuery.prototype.serializeObject=function(){var b,g,d,c,f;b=this.serializeArray();g={};d=g.hasOwnProperty;for(c=0;c softboxsum) break; + if(pay_status.is_pay && item['install']) continue; + softboxcon += '
              \ +
              \ +
              关闭推荐
              \ +
              \ +
              '+ item['title'] +'
              \ +
              \ + '+ ((item['isBuy'] && !item['install'])? + '': + 'Preview') +'\ +
              \ +
              \ +
              ' + setup_length ++; + } + } + } catch (error) { + console.log(error) + } //软件位置移动 - var softboxsum = 12; - var softboxcon = ''; if (setup_length <= softboxsum) { for (var i = 0; i < softboxsum - setup_length; i++) { softboxcon += '
              ' @@ -794,7 +821,7 @@ var index = {
              '+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) + '  '+ lan.index.release_time + (rdata.msg.is_beta == 1 ? rdata.msg.beta.uptime : rdata.msg.uptime) + '
              \
              \ -
              '+ (is_beta != 1 ? lan.index.test_version : lan.index.final_version) + lan.index.last_version_is + (result.msg.is_beta != 1 ? result.msg.beta.version : result.msg.version) + '      '+lan.index.update_time+'  ' + (is_beta != 1 ? result.msg.beta.uptime : result.msg.uptime) + '
              \ +
              '+ lan.index.last_version_is+(is_beta != 1 ? lan.index.test_version : lan.index.final_version) + (result.msg.is_beta != 1 ? result.msg.beta.version : result.msg.version) + '      '+lan.index.update_time+'  ' + (is_beta != 1 ? result.msg.beta.uptime : result.msg.uptime) + '
              \ '+ (is_beta !== 1 ? ''+lan.index.update_verison_click+''+lan.index.check_detail+'' : ''+lan.index.change_final_click+'  '+lan.index.change_final+'') + '\
              \
              \ @@ -834,11 +861,11 @@ 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 + '
              \
              \
              \ -
              '+ (is_beta !== 1 ? lan.index.test_version : lan.index.final_version) + lan.index.last_version_is + (result.msg.is_beta != 1 ? result.msg.beta.version : result.msg.version) + '   '+lan.index.update_time+'  ' + (is_beta != 1 ? result.msg.beta.uptime : result.msg.uptime) + '
              \ +
              '+ lan.index.last_version_is +(is_beta !== 1 ? lan.index.test_version : lan.index.final_version) + (result.msg.is_beta != 1 ? result.msg.beta.version : result.msg.version) + '   '+lan.index.update_time+'  ' + (is_beta != 1 ? result.msg.beta.uptime : result.msg.uptime) + '
              \ '+ (is_beta !== 1 ? ''+lan.index.update_verison_click+''+lan.index.check_detail+'' : ''+lan.index.change_final_click+''+lan.index.change_final+'') + '\
              \
              \ @@ -1068,6 +1095,7 @@ var index = { }, 100) }, open_log: function () { + return bt.open({ type: 1, area: '640px', @@ -1077,7 +1105,7 @@ var index = { shadeClose: false, content: '
              ' }); - $.get('https://www.bt.cn/Api/getUpdateLogs?type=' + bt.os, function (rdata) { + $.get('https://www.bt.cn/api/panel/updateLinuxEn', function (rdata) { var body = ''; for (var i = 0; i < rdata.length; i++) { body += '
              \ @@ -1244,7 +1272,7 @@ var index = { break; case 1: if(data.type != 'ignore'){ - bt.confirm({title:'Ignore risk',msg:'Confirm to ignore【'+ data.title +'】risk?'},function(){ + bt.confirm({title:'Ignore risk',msg:'Confirm to ignore [ '+ data.title +' ] risk?'},function(){ that.warning_set_ignore(data.model,function(res){ that.get_warning_list(false,function(){ bt.msg(res) @@ -1309,7 +1337,74 @@ var index = { if(callback) callback(res); } }); + }, + /** + * @description 获取当前的产品状态 + */ + get_product_status: function (callback) { + // var loadT = layer.msg('正在获取产品状态,请稍候...', { icon: 16, time: 0 }) + bt.send('get_pd', 'ajax/get_pd', {}, function (res) { + $('.btpro-gray').replaceWith($(res[0])); + bt.set_cookie('pro_end', res[1]); + bt.set_cookie('ltd_end', res[2]); + if(res[1] === 0){ + $(".btpro span").click(function(e){ + layer.confirm('切换回免费版可通过解绑账号实现', { icon: 3, btn: ['解绑账号'], closeBtn: 2, title: '是否取消授权' }, function () { + $.post('/ssl?action=DelToken', {}, function (rdata) { + layer.msg(rdata.msg); + setTimeout(function () { + window.location.reload(); + },2000); + }); + }); + e.stopPropagation(); + }); + } + if(callback) callback(); + }) + }, + /** + * @description 推荐进阶版产品 + */ + recommend_paid_version: function () { + try { + var recomConfig = product_recommend.get_recommend_type(0) + var pay_status = product_recommend.get_pay_status() + var is_pay = pay_status.is_pay; + var advanced = pay_status.advanced; + var end_time = pay_status.end_time; + var html = '',list_html = ''; + if(!is_pay) advanced = ''; //未购买的时候,使用推荐内容 + if(recomConfig){ + var item = recomConfig; + for (let j = 0; j < item['ps'].length; j++) { + const element = item['ps'][j]; + list_html += '
              '+ element +'
              '; + } + var pay_html = ''; + if(is_pay){ + pay_html = '
              Expired: '+ (end_time === 0?'Lifetime':(end_time === -2?'Expired':bt.format_data(end_time,'yyyy-MM-dd')) + '  Renew') +'
              ' + }else{ + pay_html = '
              ' + } + html = '
              \ +
              '+ (!is_pay?pay_html:'') +'
              ' + (is_pay?pay_html:'') +'\ +
              '+ list_html +'
              \ +
              \ +
              ' + $('#home-recommend').html(html) + } + } catch (error) { + console.log(error) + } } } index.get_init(); -//setTimeout(function () { index.get_cloud_list() }, 800); \ No newline at end of file +//setTimeout(function () { index.get_cloud_list() }, 800); + +product_recommend.init(function(){ + index.get_product_status(function(){ + index.recommend_paid_version() + }); + index.get_index_list(); +}) \ No newline at end of file diff --git a/BTPanel/static/js/public.js b/BTPanel/static/js/public.js index 9ce0870c..ef0c9688 100644 --- a/BTPanel/static/js/public.js +++ b/BTPanel/static/js/public.js @@ -334,13 +334,13 @@ var aceEditor = { title: lan.public.history_version + '[ ' + _item.fileName + ' ]', skin: 'historys_layer', content: '
              \ -
              \ - \ - \ - \ -
              ' + lan.public.file_name + '' + lan.public.v_time + '' + lan.public.operate + '
              \ -
              \ -
              ', +
              \ + \ + \ + \ +
              ' + lan.public.file_name + '' + lan.public.v_time + '' + lan.public.operate + '
              \ +
              \ +
              ', success: function(layeo, index) { var _html = ''; for (var i = 0; i < _item.historys.length; i++) { @@ -360,6 +360,7 @@ var aceEditor = { $('.recovery_file_historys').click(function() { _this.event_ecovery_file(this); }); + bt.fixed_table('historys-table'); } }); break; @@ -2963,7 +2964,9 @@ function loadScript(arry, param, callback) { var ready = 0; if (typeof param === 'function') callback = param for (var i = 0; i < arry.length; i++) { - if (!Array.isArray(bt['loadScript'])) bt['loadScript'] = [] + if (!Array.isArray(bt['loadScript'])) { + bt['loadScript'] = [] + } if (!is_file_existence(arry[i], true)) { if ((arry.length - 1) === i && callback) callback(); continue; @@ -2986,6 +2989,7 @@ function loadScript(arry, param, callback) { } else { (function(i) { script.onload = function() { + if (!bt['loadScript']) bt['loadScript'] = []; bt['loadScript'].push(arry[i]); ready++; }; @@ -3667,7 +3671,7 @@ function bindBTPanel(a, type, ip, btid, url, user, pw) { var gurl = "/config?action=AddPanelInfo"; var btaddress = $("#btaddress").val(); if (!btaddress.match(/^(http|https)+:\/\/([\w-]+\.)+[\w-]+:\d+/)) { - layer.msg(lan.bt.panel_err_format + '

              http://192.168.0.1:7800

              ', { icon: 5, time: 5000 }); + layer.msg(lan.bt.panel_err_format + '

              http://192.168.0.1:8888

              ', { icon: 5, time: 5000 }); return; } var btuser = encodeURIComponent($("#btuser").val()); @@ -3774,7 +3778,10 @@ function messagebox(){ '

              '+lan.public.exec_log+'

              ' + '
              ' + '
              ' + - '
                '+ + '
                \ +
                  \ +
                  ' + lan.public.task_long_time_not_exec + '
                  \ +
                  '+ '
                  '+ '
                  '+ '
                  ' + @@ -3797,7 +3804,7 @@ function messagebox(){ bt.send('GetExecLog','files/GetExecLog',{},function(res){ loadT.close(); var exec_log = $('#execLog'); - console.log(exec_log) + // console.log(exec_log) exec_log.html(res) exec_log[0].scrollTop = exec_log[0].scrollHeight }) @@ -3873,43 +3880,48 @@ function reader_realtime_tasks(refresh){ html = '', message = res.msg, task = res.task; - $('#taskNum').html(typeof res.task === "undefined"?0:res.task.length); + $('#taskNum').html(typeof res.task === "undefined" ? 0 : res.task.length); if(typeof res.task === "undefined"){ html = '
                  '+lan.bt.task_not_list+'
                  ' + lan.public.task_long_time_not_exec + '
                  ' + command_install_list.html(html) }else{ var shell = '', message_split = message.split("\n"); + var del_task = ''+ lan.public.del +'',loading_img = ""; for(var j = 0; j < message_split.length; j++) { shell += message_split[j] + "
                  "; } - if(command_install_list.find('li').length){ - if(command_install_list.find('li').length > res.task.length) command_install_list.find('li:eq(0)').remove(); - if(task[0].status !== '0' && !command_install_list.find('pre').length) command_install_list.find('li:eq(0)').append('
                  ' + shell +'
                  ') - messageBoxWssock.el = command_install_list.find('pre'); - }else{ - for (var i = 0; i < task.length; i++) { - var item = task[i], task_html = '', del_task = ''+ lan.public.del +'',loading_img = ""; - if(item.status === '-1' && item.type === 'download'){ - task_html = "
                  " + item.name + "" + (ToSize(message.used) + "/" + ToSize(message.total)) + "" + message.pre + "%"+ lan.bt.task_downloading +" "+ loading_img +" | "+ del_task +""; - }else{ - task_html += '' + item.name + ''; - task_html += ''; - if(item.status !== "-1"){ - task_html += lan.bt.task_sleep + ' | ' + del_task; - }else{ - var is_scan = item.name.indexOf("扫描") !== -1; - task_html += (is_scan?lan.bt.task_scan:lan.bt.task_install) + ' ' + loading_img + ' | ' + del_task; - } - task_html += ""; - if(item.type !== "download" && item.status === "-1"){ - task_html += '
                  ' + shell +'
                  ' - } + // if(command_install_list.find('li').length){ + // if(command_install_list.find('li').length > res.task.length) command_install_list.find('li:eq(0)').remove(); + // if(task[0].status !== '0' && !command_install_list.find('pre').length) command_install_list.find('li:eq(0)').append('
                  ' + shell +'
                  ') + // messageBoxWssock.el = command_install_list.find('pre'); + // }else{ + for (var i = 0; i < task.length; i++) { + var item = task[i], task_html = ''; + if(item.status === '-1' && item.type === 'download'){ + task_html = "
                  " + item.name + "" + (ToSize(message.used) + "/" + ToSize(message.total)) + "" + message.pre + "%"+ lan.bt.task_downloading +" "+ loading_img +" | "+ del_task.replace('$id', item.id) +""; + }else{ + task_html += '' + item.name + ''; + task_html += ''; + switch(item.status){ + case '0': + task_html += lan.bt.task_sleep + ' | ' + del_task.replace('$id', item.id); + break + case '-1': + var is_scan = item.name.indexOf("扫描") !== -1; + task_html += (is_scan ? lan.bt.task_scan : lan.bt.task_install) + ' ' + loading_img + ' | ' + del_task.replace('$id', item.id); + break + } + task_html += ""; + if(item.type !== "download" && item.status === "-1"){ + task_html += '
                  ' + shell +'
                  ' } - html += "
                • "+ task_html +"
                • "; } - command_install_list.find('ul').append(html); + html += "
                • "+ task_html +"
                • "; } - if(task[0].status === '0'){ + command_install_list.find('ul').html(html); + // } + if(task.length > 0 && task[0].status === '0'){ setTimeout(function(){ reader_realtime_tasks(true) },100) @@ -5188,4 +5200,148 @@ var MessageChannel = { }) } } -/** 消息通道 end**/ \ No newline at end of file +/** 消息通道 end**/ +var product_recommend = { + data:null, + /** + * @description 初始化 + */ + init:function(callback){ + var _this = this; + if(location.pathname.indexOf('bind') > -1) return; + this.get_product_type(function (rdata) { + _this.data = rdata + if(callback) callback(rdata) + }) + }, + /** + * @description 获取推荐类型 + * @param {object} type 参数{type:类型} + */ + get_recommend_type:function(type){ + var config = null,pathname = location.pathname.replace('/','') || 'home'; + for (var i = 0; i < this.data.length; i++) { + var item = this.data[i]; + if(item.type == type && item.show) config = item + } + return config + }, + + /** + * @description 或指定版本事件 + * @param {} name + */ + get_version_event:function (item,param) { + var pay_status = this.get_pay_status(); + bt.soft.get_soft_find(item.name,function(res){ + if((res.type === 12 && pay_status.is_pay && pay_status.advanced !== 'ltd') || !pay_status.is_pay){ + product_recommend.recommend_product_view(item) + }else if(!res.setup){ + bt.soft.install(item.name) + }else{ + bt.plugin.get_plugin_byhtml(item.name,function(html){ + if(typeof html === "string"){ + layer.open({ + type:1, + shade:0, + skin:'hide', + content:html, + success:function(){ + var is_event = false; + for (var i = 0; i < item.eventList.length; i++) { + var data = item.eventList[i]; + var oldVersion = data.version.replace('.',''),newVersion = res.version.replace('.',''); + if(newVersion <= oldVersion){ + is_event = true + setTimeout(function () { + new Function(data.event.replace('$siteName',param))() + },100) + break; + } + } + if(!is_event) new Function(item.eventList[item.eventList.length - 1].event.replace('$siteName',param))() + } + }) + } + }) + } + + }) + }, + /** + * @description 获取支付状态 + */ + get_pay_status:function(){ + var pro_end = parseInt(bt.get_cookie('pro_end') || -1); + var ltd_end = parseInt(bt.get_cookie('ltd_end') || -1); + var is_pay = pro_end > -1 || ltd_end > -1; // 是否购买付费版本 + var advanced = 'pro'; // 已购买,专业版优先显示 + if(pro_end === -2 || pro_end > -1) advanced = 'pro'; + if(ltd_end === -2 || ltd_end > -1) advanced = 'ltd'; + var end_time = advanced === 'ltd'? ltd_end:pro_end; // 到期时间 + return { advanced: advanced, is_pay:is_pay, end_time:end_time }; + }, + + pay_product_sign:function (type,source) { + switch (type) { + case 'pro': + bt.soft['updata_' + type](source); + break; + case 'ltd': + bt.soft['updata_' + type](false, source); + break; + } + }, + /** + * @description 获取项目类型 + * @param {Function} callback 回调函数 + */ + get_product_type:function(callback){ + bt.send('get_pay_type','ajax/get_pay_type',{},function(rdata){ + bt.set_storage('session','get_pay_type',JSON.stringify(rdata)) + if(callback) callback(rdata) + }) + }, + /** + * @description 推荐购买产品 + * @param {Object} pay_id 购买的入口id + */ + recommend_product_view: function (config) { + var name = config.name.split('_')[0]; + var status = this.get_pay_status(); + console.log(status); + bt.open({ + title:false, + area:'650px', + btn:false, + content:'
                  \ +
                  \ +
                  \ +
                  '+ config.pluginName +'
                  \ +
                  '+ config.ps +'
                  \ +
                  \ + 产品预览\ +
                  \ +
                  \ +
                  ', + success:function () { + // 产品预览 + $('.product_view img').click(function () { + layer.open({ + type:1, + title:'查看图片', + area:['650px','450px'], + closeBtn:2, + btn:false, + content:'' + }) + }) + // 立即购买 + $('.buyNow').click(function(){ + bt.set_cookie('pay_source',config.pay) + bt.soft['updata_' + status.advanced]() + }) + } + }) + } +} \ No newline at end of file diff --git a/BTPanel/static/js/public_backup.js b/BTPanel/static/js/public_backup.js index bbb8ec35..303a2c29 100644 --- a/BTPanel/static/js/public_backup.js +++ b/BTPanel/static/js/public_backup.js @@ -329,16 +329,16 @@ var bt = { title: type === 'all' ? 'Select directories or files' : lan.bt.dir, closeBtn: 2, shift: 5, - content: "
                  " + lan.bt.path + ":
                  " + lan.bt.comp + "
                    " + lan.bt.filename + "" + lan.bt.etime + "" + lan.bt.access + "" + lan.bt.own + "
                    ", - success: function() { - $('#btn_back').click(function() { + content: "
                    " + lan.bt.path + ":
                    " + lan.bt.comp + "
                      " + lan.bt.filename + "" + lan.bt.etime + "" + lan.bt.access + "" + lan.bt.own + "
                      ", + success: function () { + $('#btn_back').click(function () { var path = $("#PathPlace").find("span").text(); path = bt.rtrim(bt.format_path(path), '/'); var back_path = bt.get_file_path(path); _this.get_file_list(back_path, type); }) //选择 - $('#bt_select').on('click',function () { + $('#bt_select').on('click', function () { var path = bt.format_path($("#PathPlace").find("span").text()); if(type === 'file' && !$('#tbody tr.active').length){ layer.msg('Select the file first!',{icon:0}) @@ -361,6 +361,7 @@ var bt = { paths = default_path; } _this.get_file_list(paths, type); + bt.fixed_table('file-list-table'); } }); _this.set_cookie('ChangePath', loadT.form); @@ -627,33 +628,32 @@ var bt = { return param; }, msg: function(config) { - var btns = new Array(); - var btnObj = { - title: config.title ? config.title : false, - shadeClose: config.shadeClose ? config.shadeClose : true, - closeBtn: config.closeBtn ? config.closeBtn : 2, - scrollbar: true, - shade: 0.3 - }; - if (!config.hasOwnProperty('time')) config.time = 0; - if (typeof config.msg == 'string' && bt.contains(config.msg, 'ERROR')) config.time = 0; - - if (config.hasOwnProperty('icon')) { - if (typeof config.icon == 'boolean') config.icon = config.icon ? 1 : 2; - } else if (config.hasOwnProperty('status')) { - config.icon = config.status ? 1 : 2; - if (!config.status) { - btnObj.time = 0; - } - } - if (config.icon) btnObj.icon = config.icon; - btnObj.time = config.time; - var msg = '' - if (config.msg) msg += config.msg; - if (config.msg_error) msg += config.msg_error; - if (config.msg_solve) msg += config.msg_solve; - if(config.status) $.extend(btnObj,{closeBtn:0,time:2000}); - layer.msg(msg, btnObj); + var btnObj = { + title: config.title ? config.title : false, + shadeClose: config.shadeClose ? config.shadeClose : true, + closeBtn: config.closeBtn ? config.closeBtn : 0, + scrollbar: true, + shade: 0.3, + }; + if (!config.hasOwnProperty('time')) config.time = 2000; + if (typeof config.msg == 'string' && bt.contains(config.msg, 'ERROR')) config.time = 0; + + if (config.hasOwnProperty('icon')) { + if (typeof config.icon == 'boolean') config.icon = config.icon ? 1 : 2; + } else if (config.hasOwnProperty('status')) { + config.icon = config.status ? 1 : 2; + if (!config.status) { + btnObj.time = 0; + } + } + if (config.icon) btnObj.icon = config.icon; + btnObj.time = config.time; + var msg = '' + if (config.msg) msg += config.msg; + if (config.msg_error) msg += config.msg_error; + if (config.msg_solve) msg += config.msg_solve; + + layer.msg(msg, btnObj); }, confirm: function(config, callback, callback1) { var btnObj = { @@ -955,7 +955,10 @@ var bt = { title: data.title, closeBtn: 2, content: _form.prop("outerHTML"), - end: data.end ? data.end : false + end: data.end ? data.end : false, + success:function(){ + if (data.success) data.success() + } }) setTimeout(function() { bt.render_clicks(clicks, loadOpen, callback); @@ -1754,14 +1757,11 @@ bt.index = { icon: 1 }); setTimeout(function() { - task() - }, 1000) - }); - - //InstallTips(); - fly("onekey") - }) - } + task(); + }, 1000); + }); + }); + } } bt.weixin = { @@ -2006,7 +2006,7 @@ bt.recycle_bin = { bt.open({ type: 1, skin: 'demo-class', - area: ['80%', '606px'], + area: ['80%', '672px'], title: lan.files.recycle_bin_title, closeBtn: 2, shift: 5, @@ -2030,7 +2030,7 @@ bt.recycle_bin = {
                      \
                      \
                      \ -
                      \ +
                      \
                      \
                      ' }); @@ -2939,7 +2939,7 @@ bt.config = { }); }, set_panel_ssl: function(status, callback) { - var msg = status ? lan.config.ssl_close_msg : '' + lan.config.ssl_open_ps + '
                    • ' + lan.config.ssl_open_ps_1 + '
                    • ' + lan.config.ssl_open_ps_2 + '
                    • ' + lan.config.ssl_open_ps_3 + '
                    • ' + lan.config.ssl_open_ps_5 + '

                      '; + var msg = status ? lan.config.ssl_close_msg : '' + lan.config.ssl_open_ps + '
                    • ' + lan.config.ssl_open_ps_1 + '
                    • ' + lan.config.ssl_open_ps_2 + '
                    • ' + lan.config.ssl_open_ps_3 + '
                    • ' + lan.config.ssl_open_ps_5 + '

                      '; layer.confirm(msg, { title: lan.config.ssl_title, closeBtn: 2, @@ -3405,10 +3405,10 @@ bt.firewall = { action = "AddAcceptPort"; } - if (ps.length < 1) { - layer.msg(lan.firewall.ps_err, { icon: 2 }); - return -1; - } + // if (ps.length < 1) { + // layer.msg(lan.firewall.ps_err, { icon: 2 }); + // return -1; + // } loading = bt.load(); bt.send(action, 'firewall/' + action, { port: port, type: type, ps: ps }, function(rdata) { loading.close(); @@ -3853,113 +3853,138 @@ bt.soft = { if(callback) callback(res) }); }, - set_product_renew_status:function(data,callback){ - $.post('/message/status_message',{id:data.id,state:data.state},function(res){ - if(res.status === false){ - layer.msg(res.msg,{icon:2}); - return false; - } - if(callback) callback(res) - }); - }, - // 产品支付视图(配置参数) - product_pay_view:function(config){ - if(!bt.get_cookie('bt_user_info')){ - bt.pub.bind_btname(function(){ + set_product_renew_status: function (data, callback) { + $.post('/message/status_message', {id: data.id, state: data.state}, function (res) { + if (res.status === false) { + layer.msg(res.msg, {icon: 2}); + return false; + } + if (callback) callback(res) + }); + }, + // 产品支付视图(配置参数) + product_pay_view: function (config) { + if (!bt.get_cookie('bt_user_info')) { + bt.pub.bind_btname(function () { window.location.reload(); }); return false; } - if(config.renew === -1) config.renew = false - if(typeof config == "string") config = JSON.parse(config); - config = $.extend({ - plugin:null, - renew:null, - active:'', - type:'', - pro: parseInt(bt.get_cookie('pro_end')) || -1, - ltd: parseInt(bt.get_cookie('ltd_end')) || -1} - ,config); - var title = '',that = this,endTime = null; - if(!config.is_alone){ - if(config.plugin){ // 条件:当前为插件 - title = (!config.renew?'Buy ':'续费') + config.name; - ndTime = !config.renew?config.renew:null - }else if(config.pro == -1 && config.ltd == -1){ // 条件:专业版和企业版都没有购买过 - title = 'Upgrade to Pro, all plugins are free to use'; - }else if(config.ltd > 0){ // 条件:企业版续费 - title = 'Renew ' +(config.name == ''?'宝塔专业版':config.name); - endTime = config.ltd; - }else if(config.pro > 0 || config.pro == -2){ // 条件:专业版续费 - title = 'Renew ' + (config.name == ''?'宝塔专业版':config.name); - endTime = config.pro; - }else if(config.ltd == -2){ - title = 'Renew ' + (config.name == ''?'宝塔专业版':config.name); - endTime = config.ltd; - } - }else{ - title = (config.ltd>0?'续费':'购买')+'宝塔企业版'; - } - bt.open({ - type:1, - title:title, - area:['650px','760px'], - shadeClose:false, - content:'
                      \ -
                      \ - Account: \ - Expire date:\ -
                      \ -
                      \ -
                      Type
                      \ -
                      \ -
                      \ -
                      \ -
                      Payment method
                      \ -
                      \ -
                      \ -
                      \ -
                      Choose your plan
                      \ -
                      \ -
                      \ -
                      \ -
                      \ -
                      ', - success:function(indexs,layers){ - $.getScript('https://js.stripe.com/v3/') + if (config.renew === -1) config.renew = false + if (typeof config == "string") config = JSON.parse(config); + config = $.extend({ + plugin: null, + renew: null, + active: '', + type: '', + pro: parseInt(bt.get_cookie('pro_end')) || -1, + ltd: parseInt(bt.get_cookie('ltd_end')) || -1 + }, config); + var totalNum = config.totalNum ? config.totalNum : ''; + if (totalNum) { + bt.set_cookie('pay_source', parseInt(totalNum)); + } + var title = '',that = this,endTime = null; + if (!config.is_alone) { + // 条件:当前为插件 + if (config.plugin) { + title = (!config.renew ? 'Buy ' : '续费') + config.name; + ndTime = !config.renew ? config.renew : null + } else if (config.pro == -1 && config.ltd == -1) { // 条件:专业版和企业版都没有购买过 + title = 'Upgrade to Pro, all plugins are free to use'; + } else if (config.ltd > 0) { // 条件:企业版续费 + title = 'Renew ' + (config.name == '' ? '宝塔专业版' : config.name); + endTime = config.ltd; + } else if (config.pro > 0 || config.pro == -2) { // 条件:专业版续费 + title = 'Renew ' + (config.name == '' ? '宝塔专业版' : config.name); + endTime = config.pro; + } else if (config.ltd == -2) { + title = 'Renew ' + (config.name == '' ? '宝塔专业版' : config.name); + endTime = config.ltd; + } + } else { + title = (config.ltd > 0 ? '续费' : '购买') + '宝塔企业版'; + } + bt.open({ + type: 1, + title: title, + area: ['650px', '760px'], + shadeClose: false, + content: '\ +
                      \ +
                      \ + Account: \ + Expire date: \ +
                      \ +
                      \ +
                      Type
                      \ +
                      \ +
                      \ +
                      \ +
                      Payment method
                      \ +
                      \ +
                      \ +
                      \ +
                      Choose your plan
                      \ +
                      \ +
                      \ +
                      \ +
                      \ +
                      \ + ', + success: function ($layer, index) { + $.getScript('https://js.stripe.com/v3/'); var bt_user_info = bt.get_cookie('bt_user_info'); - if(!bt_user_info){ - bt.pub.get_user_info(function(res){ - $('.bindUser span').html(res.data.username +'Change'); + if (!bt_user_info) { + bt.pub.get_user_info(function (res) { + $('.bindUser span').html(res.data.username + 'Change'); }); - }else{ - $('.bindUser span').html(JSON.parse(bt_user_info).data.username +'Change'); + } else { + $('.bindUser span').html(JSON.parse(bt_user_info).data.username + 'Change'); } - endTime != null?$('.endTime span').html(endTime > parseInt(new Date().getTime() /1000)?bt.format_data(endTime):'Expired'):$('.endTime').hide(); - $('.bindUser').on('click','a',function(){ - bt.pub.bind_btname(function(){ - bt.soft.product_pay_view(config); - }); - }); + if (endTime != null) { + $('.endTime span').html(endTime > parseInt(new Date().getTime() / 1000) ? bt.format_data(endTime) : 'Expired'); + } else { + $('.endTime').hide(); + } + $('.bindUser').on('click', 'a', function () { + bt.pub.bind_btname(function () { + bt.soft.product_pay_view(config); + }); + }); var arry = []; - //config.name = '堡塔企业级防篡改'; - if(config.plugin) arry.push({title:config.name,name:config.name,ps:'Plug-in only',pid:config.pid,renew:config.renew || false,active:((config.pro < 0 && config.ltd < 0) || (config.type == 12 && config.ltd<0)?true:false)}); - if((((config.pro > 0 || config.pro == -2 || config.ltd < 0) && ((config.ltd > 0 && config.ltd != config.pro) || config.ltd < 0) && config.type != 12) || config.limit == 'pro' || (config.ltd < 0 && config.pro == -1)) && config.type != 12 && ((config.ltd < 0 && config.pro > 0) || (config.ltd < 0 && config.pro < 0))){ - arry.push({title:'',name:'',pid:'100000058',ps:'Recommended',renew:config.renew || false,active: (((config.type == 8 && !config.plugin) || config.limit == 'pro' || (config.pro > 0 || config.pro == -2)) && config.ltd < 0 && (config.ltd == -2?(config.pro == -2?false:true):true))}); - } + //config.name = '堡塔企业级防篡改'; + if (config.plugin) arry.push({ + title: config.name, + name: config.name, + ps: 'Plug-in only', + pid: config.pid, + renew: config.renew || false, + active: ((config.pro < 0 && config.ltd < 0) || (config.type == 12 && config.ltd < 0) ? true : false) + }); + if ((((config.pro > 0 || config.pro == -2 || config.ltd < 0) && ((config.ltd > 0 && config.ltd != config.pro) || config.ltd < 0) && config.type != 12) || config.limit == 'pro' || (config.ltd < 0 && config.pro == -1)) && config.type != 12 && ((config.ltd < 0 && config.pro > 0) || (config.ltd < 0 && config.pro < 0))) { + arry.push({ + title: '', + name: '', + pid: '100000058', + ps: 'Recommended', + renew: config.renew || false, + active: (((config.type == 8 && !config.plugin) || config.limit == 'pro' || (config.pro > 0 || config.pro == -2)) && config.ltd < 0 && (config.ltd == -2 ? (config.pro == -2 ? false : true) : true)) + }); + } - // if((((config.ltd > 0 || config.ltd == -2 || config.pro == -2) || (config.ltd == -1 && config.pro == -1) || config.limit == 'ltd') && (config.pro < 0 || (config.pro >= 0 && config.ltd >0))) || (config.is_alone && config.pid == 100000032)){ - // arry.push({title:'',name:'宝塔面板企业版',ps:'推荐企业购买',pid:100000032,recommend:true,active:((config.type == 12 && !config.plugin && config.ltd > 0) || config.limit == 'ltd' || (config.renew == config.ltd && config.ltd > 0)? true:false)}); - // } - // if(config.type == 12 && config.pro >= 0 && config.ltd < 0) $('.pro-tips').html('温馨提示:专业版升级企业版需要手动结算当前专业版授权,《专业版和企业版切换教程》').css('color','red'); - $('#libPay-type .li-con').append(that.product_pay_swicth('type',arry)); - if(config.source) bt.set_cookie('pay_source',config.source); - that.each(arry,function(index,item){ - if(item.active){ - that.product_pay_page_refresh($.extend({condition:1},item)); - } - }); - }, + // if((((config.ltd > 0 || config.ltd == -2 || config.pro == -2) || (config.ltd == -1 && config.pro == -1) || config.limit == 'ltd') && (config.pro < 0 || (config.pro >= 0 && config.ltd >0))) || (config.is_alone && config.pid == 100000032)){ + // arry.push({title:'',name:'宝塔面板企业版',ps:'推荐企业购买',pid:100000032,recommend:true,active:((config.type == 12 && !config.plugin && config.ltd > 0) || config.limit == 'ltd' || (config.renew == config.ltd && config.ltd > 0)? true:false)}); + // } + // if(config.type == 12 && config.pro >= 0 && config.ltd < 0) $('.pro-tips').html('温馨提示:专业版升级企业版需要手动结算当前专业版授权,《专业版和企业版切换教程》').css('color','red'); + $('#libPay-type .li-con').append(that.product_pay_swicth('type', arry)); + if (config.source) bt.set_cookie('pay_source', config.source); + that.each(arry, function (index, item) { + if (item.active) { + that.product_pay_page_refresh($.extend({condition: 1}, item)); + } + }); + }, end:function(){ clearInterval(bt.soft.pub.wxpayTimeId); bt.clear_cookie('pay_source'); @@ -3986,95 +4011,107 @@ bt.soft = { } }, // 产品页面刷新 - product_pay_page_refresh: function (config){ - var condition = config.condition, that = this; - switch (condition){ - case 1: - var loadT = bt.load(); - bt.send('get_product_auth', 'auth/get_product_auth', { page: 1,pageSize:15 }, function(res) { - bt.soft.pro.get_voucher(config.pid, function (rdata) { - loadT.close(); - var _arry = [{ title: '微信支付', condition: 2 },{ title: 'Stripe', condition: 3 }, { title: 'voucher', condition: 4 },{title: "Authorization", condition: 7,"_pid":config.pid}]; - if(config.renew){ - _arry.splice(_arry.length-1,1) - _arry[rdata.length > 0 ? '2' : '1'].active = true - config.condition = rdata.length > 0?4:2 - }else{ - _arry[res.length > 0? '3' :(rdata.length > 0 ? '2' : '1')].active = true; - config.condition = (res.length > 0?7:(rdata.length > 0?4:2)) - } - if(res == null) res = []; - if (rdata == null) rdata = []; - $('#libPay-mode .li-con').empty().append( - that.product_pay_swicth('payment', { - name: config.name, - pid: config.pid, - data: _arry, + product_pay_page_refresh: function (config) { + var that = this; + var condition = config.condition; + switch (condition) { + case 1: + var loadT = bt.load(); + bt.send('get_product_auth', 'auth/get_product_auth', {page: 1, pageSize: 15,pid:config.pid}, function (res) { + bt.soft.pro.get_voucher(config.pid, function (rdata) { + loadT.close(); + var _arry = [ + {title: '微信支付', condition: 2}, + {title: 'Stripe', condition: 3}, + {title: 'voucher', condition: 4}, + {title: "Authorization", condition: 7, "_pid": config.pid} + ]; + // if (config.renew) { + // _arry.splice(_arry.length - 1, 1); + // _arry[rdata.length > 0 ? '2' : '1'].active = true; + // config.condition = rdata.length > 0 ? 4 : 2; + // } else { + _arry[res.length > 0 ? '3' : (rdata.length > 0 ? '2' : '1')].active = true; + config.condition = (res.length > 0 ? 7 : (rdata.length > 0 ? 4 : 3)) + // } + if (res == null) res = []; + if (rdata == null) rdata = []; + $('#libPay-mode .li-con').empty().append( + that.product_pay_swicth('payment', { + name: config.name, + pid: config.pid, + data: _arry, voucher_data: rdata }) ); - if(config.renew){ - if (rdata.length > 0) config.voucher_data = rdata; - }else{ - if (rdata.length > 0) config.voucher_data = rdata; - if(res.length > 0) config.voucher_data = res; - } + if (config.renew) { + if (rdata.length > 0) config.voucher_data = rdata; + } else { + if (rdata.length > 0) config.voucher_data = rdata; + if (res.length > 0) config.voucher_data = res; + } that.product_pay_page_refresh(config); }); }); break; - case 2: - case 3: - $('#libPay-content .li-tit').text('Choose your plan'); - config.pay = condition; - if (config.pid == '100000030') { - $('#libPay-tips').show(); - } else { - $('#libPay-tips').hide(); - } - var loadT = bt.load(); - bt.soft.get_product_discount_cache(config,function (rdata){ - loadT.close(); - var _arry = [],index = 0; - try { - delete rdata.pid - } catch (error) { - console.log(rdata.pid); - } - that.each(rdata, function (key, item) { - _arry.push($.extend({ cycle: parseInt(key) }, item)); - }); - _arry[index].active = true; - $('#libPay-content .li-con').empty().append( - that.product_pay_swicth('time',{ name: config.name, pid: config.pid, data: _arry}) - ); - config.condition = 5; - config = $.extend(config, _arry[index]); - that.product_pay_page_refresh(config); - }); - break; - case 4: - var loadP = bt.load("Getting deduction volume information!") - clearInterval(bt.soft.pub.wxpayTimeId); - $('#libPay-content .li-tit').text('Vouchers'); - $('#libPay-pay').removeAttr('data-qecode'); - $('#libPay-tips').hide(); - function callback(rdata) { - loadP.close() - if(rdata == null) rdata = []; - if(rdata.length == 0){ - $('#libPay-content .li-con').empty() - that.product_pay_page_refresh({ condition: 6, pid: '', code: false }); - return false; - } + case 2: + case 3: + $('#libPay-content .li-tit').text('Choose your plan'); + config.pay = condition; + if (config.pid == '100000030') { + $('#libPay-tips').show(); + } else { + $('#libPay-tips').hide(); + } + var loadT = bt.load(); + bt.soft.get_product_discount_cache(config, function (rdata) { + loadT.close(); + var _arry = []; + var index = 0; + try { + delete rdata.pid + } catch (error) { + console.log(rdata.pid); + } + that.each(rdata, function (key, item) { + _arry.push($.extend({cycle: parseInt(key)}, item)); + }); + _arry[index].active = true; + $('#libPay-content .li-con').empty().append( + that.product_pay_swicth('time', { + name: config.name, pid: config.pid, data: _arry + }) + ); + config.condition = 5; + config = $.extend(config, _arry[index]); + that.product_pay_page_refresh(config); + }); + break; + case 4: + var loadP = bt.load("Getting deduction volume information!") + clearInterval(bt.soft.pub.wxpayTimeId); + $('#libPay-content').empty().append('
                      '); + $('#libPay-pay').empty(); + $('#libPay-content .li-tit').text('Vouchers'); + $('#libPay-pay').removeAttr('data-qecode'); + $('#libPay-tips').hide(); + + function callback(rdata) { + loadP.close(); + if (rdata == null && !Array.isArray(rdata)) rdata = []; + if (rdata.length == 0) { + $('#libPay-content .li-con').empty() + that.product_pay_page_refresh({condition: 6, pid: '', code: false}); + return false; + } rdata[0].active = true; - $('#libPay-content .li-con').empty().append( - that.product_pay_swicth('voucher', { - name: config.name, - pid: config.pid, - data: rdata - }) - ); + $('#libPay-content .li-con').append( + that.product_pay_swicth('voucher', { + name: config.name, + pid: config.pid, + data: rdata + }) + ); config.condition = 6; that.product_pay_page_refresh($.extend(config, rdata[0])); } @@ -4082,7 +4119,7 @@ bt.soft = { callback(config.voucher_data); } else { bt.soft.pro.get_voucher(config.pid, function (rdata) { - callback(rdata); + callback(rdata); }); } break; @@ -4103,8 +4140,15 @@ bt.soft = { }else{ $('#libPay-pay').html('
                      Loading, please wait!
                      '); } - var paream = {pid:config.pid,cycle:config.cycle},pay_source = bt.get_cookie('pay_source'); - if(pay_source) paream.source = bt.get_cookie('pay_source'); + var paream = {pid:config.pid,cycle:config.cycle}; + paream.source = parseInt(bt.get_cookie('pay_source') || 0) + if (paream.source === 0) { + if ($('.btpro-gray').length == 1) { // 是否免费版 + paream.source = 27; + } else { + paream.source = 28; + } + } if(!paream.pid) delete paream.pid; if(bt.get_cookie('serial_no') != null) paream.serial_no = config.serial_no || bt.get_cookie('serial_no') that.pro.create_order(paream,function (rdata){ @@ -4123,45 +4167,48 @@ bt.soft = { var stripe = Stripe($(this).data('keys')); that.pro.get_check_out_info($(this).data('code'),function(res){ loadT.close() - if(res.id){ - stripe.redirectToCheckout({ sessionId: res.id }); - }else{ - layer.msg("Payment order failed, please contact administrator!", { icon: 2 }); + if (res.id) { + stripe.redirectToCheckout({sessionId: res.id}); + } else { + layer.msg("Payment order failed, please contact administrator!", {icon: 2}); } }) }) - break; - case 6: - var _html = $('
                      '), _button = $(''); - _button.click(function (ev) { - if (!config.serial_no){ - layer.msg('No vouchers'); - return false; - } - bt.soft.pro.create_order_voucher(config.pid, config.code, config.id, config.cycle, config.cycle_unit , config.charge_type,function (rdata) { - layer.closeAll(); - bt.set_cookie('force', 1); - if (soft) soft.flush_cache(); - bt.msg(rdata.res); - }); - }); - $('#libPay-pay').empty().append(_html.append(_button)); - break; - case 7: - if(config.renew) return false - var loadA = bt.load("Obtaining authorization information!") - var _pid = config.pid; - $('#libPay-content .li-tit').text('Authorization information'); - bt.send('get_product_auth', 'auth/get_product_auth', { page: 1,pageSize:15 }, function(res) { - loadA.close(); - if(res.status==false){ - layer.msg(res.msg, { icon: res.status ? 1 : 2 }); - return false; - } - _html = $('
                        '); - if(res.length == 0){ - _html.append($('
                      • No authorization
                      • ')); - } + break; + case 6: + var _html = $('
                        '); + var _button = $(''); + _button.click(function (ev) { + if (!config.serial_no) { + layer.msg('No vouchers'); + return false; + } + bt.soft.pro.create_order_voucher(config.pid, config.code, config.id, config.cycle, config.cycle_unit, config.charge_type, function (rdata) { + layer.closeAll(); + bt.set_cookie('force', 1); + if (soft) soft.flush_cache(); + bt.msg(rdata.res); + }); + }); + $('#libPay-pay').empty().append(_html.append(_button)); + break; + case 7: + if (config.renew) return false + var loadA = bt.load("Obtaining authorization information!") + $('#libPay-content').empty().append('
                        '); + $('#libPay-pay').empty(); + var _pid = config.pid; + $('#libPay-content .li-tit').text('Authorization information'); + bt.send('get_product_auth', 'auth/get_product_auth', {page: 1, pageSize: 15,pid:config.pid}, function (res) { + loadA.close(); + if (res.status == false) { + layer.msg(res.msg, {icon: res.status ? 1 : 2}); + return false; + } + _html = $('
                          '); + if (res.length == 0) { + _html.append($('
                        • No authorization
                        • ')); + } var authorization_flag = false; $.each(res, function (index, item) { if(_pid == item.product_id){ @@ -4169,34 +4216,35 @@ bt.soft = { authorization_flag = true; } if(authorization_flag == false && index == res.length - 1){ - _html.append($('
                        • No authorization
                        • ')); + _html.append($('
                        • No authorization
                        • ')); } }); - $('#libPay-content .li-con').empty().append(_html); - $('#libPay-content ul li').click(function(){ - $(this).addClass('active').siblings().removeClass('active') - }); - $('#libPay-pay').empty().append($('
                          ')); - //授权 - $('#authorization').unbind(); - if($('#libPay-content .li-con ul li span').html().indexOf("No authorization")>0){ - $('#authorization').attr("disabled","disabled"); - }else{ - $('#authorization').removeAttr("disabled"); - $('#authorization').on('click',function(e){ - var loadU = bt.load("Under licensing!") - var _serial_no = $('#libPay-content .active').attr('data-id'); - if(typeof _serial_no ==undefined) return false; - bt.send('auth_activate', 'auth/auth_activate', { serial_no: _serial_no }, function(res) { - loadU.close() - if(res){ - layer.msg(res.msg, { icon: res.status ? 1 : 2 }); - if(res.status){ - window.location.reload(); - } - } + $('#libPay-content .li-con').empty().append(_html); + $('#libPay-content ul li').click(function () { + $(this).addClass('active').siblings().removeClass('active') }); - }) + $('#libPay-pay').empty().append($('
                          ')); + //授权 + $('#authorization').unbind(); + var html = $('#libPay-content .li-con ul li span').html() || ''; + if (html.indexOf("No authorization") > 0) { + $('#authorization').attr("disabled", "disabled"); + } else { + $('#authorization').removeAttr("disabled"); + $('#authorization').on('click', function (e) { + var _serial_no = $('#libPay-content .active').attr('data-id'); + if (typeof _serial_no == 'undefined') return false; + var loadU = bt.load("Under licensing!"); + bt.send('auth_activate', 'auth/auth_activate', {serial_no: _serial_no}, function (res) { + loadU.close() + if (res) { + layer.msg(res.msg, {icon: res.status ? 1 : 2}); + if (res.status) { + window.location.reload(); + } + } + }); + }) } }); break; @@ -4235,26 +4283,30 @@ bt.soft = { }); break; case 'time': // 产品开通时长(配置参数) - _html = $('
                            '); - this.each(config.data, function (index, item) { - _html.append($('
                          • ' + that.pro.conver_unit(item.cycle + item.cycle_unit) + '' + (item.discount_rate != 1 ? '' + (100-item.discount_rate * 100) + '% off' : '') + '
                          • ').data($.extend({ pid: config.pid, dom_index: index }, item)).click(function (ev) { - var data = $(this).data(); - if (!$(this).hasClass('active')) that.product_pay_page_refresh($.extend({ condition: 5 }, data)); - $(this).addClass('active').siblings().removeClass('active'); - })); - }); - break; - case 'voucher':// 产品抵扣卷(配置参数) - _html = $('
                              '); - this.each(config.data, function (index, item){ - _html.append($('
                            • ' + (item.cycle_unit == 'month' && item.cycle == 999 ? '永久' : (item.cycle + that.pro.conver_unit(item.cycle_unit))) + '
                            • ').data($.extend({ pid: config.pid }, item)).click(function (ev) { - var data = $(this).data(); - $(this).addClass('active').siblings().removeClass('active'); - that.product_pay_page_refresh($.extend({ condition: 6 }, data)); - })); - }); - break; - case 'wechat': + _html = $('
                                '); + this.each(config.data, function (index, item) { + _html.append($('
                              • ' + that.pro.conver_unit(item.cycle + item.cycle_unit) + '' + (item.discount_rate != 1 ? '' + (100 - item.discount_rate * 100) + '% off' : '') + '
                              • ').data($.extend({ + pid: config.pid, + dom_index: index + }, item)).click(function (ev) { + var data = $(this).data(); + if (!$(this).hasClass('active')) that.product_pay_page_refresh($.extend({condition: 5}, data)); + $(this).addClass('active').siblings().removeClass('active'); + })); + }); + break; + case 'voucher':// 产品抵扣卷(配置参数) + _html = $('
                                  '); + + this.each(config.data, function (index, item) { + _html.append($('
                                • ' + (item.cycle_unit == 'month' && item.cycle == 999 ? '永久' : (item.cycle + that.pro.conver_unit(item.cycle_unit))) + '
                                • ').data($.extend({pid: config.pid}, item)).click(function (ev) { + var data = $(this).data(); + $(this).addClass('active').siblings().removeClass('active'); + that.product_pay_page_refresh($.extend({condition: 6}, data)); + })); + }); + break; + case 'wechat': case 'alipay': _html = $('
                                  ' + 'Total' + @@ -4331,20 +4383,27 @@ bt.soft = { } return that; }, - /** - * @description 升级专业版 - */ - updata_pro:function(){ - bt.soft.product_pay_view({name:'Pro',pid:'100000058',limit:'pro'}); - }, - /** - * @description 续费专业版 - */ - renew_pro:function(){ - var config = {name:'Pro',pid:'100000058',limit:'pro',renew:true} - if(!bt.get_cookie('serial_no')) delete config.renew - bt.soft.product_pay_view(config); - }, + /** + * @description 升级专业版 + */ + updata_pro: function (num) { + var param = { + name: 'Pro', + pid: 100000058, + limit: 'pro' + } + if(num) param['totalNum'] = num; + bt.set_cookie('pay_source', num); + bt.soft.product_pay_view(param); + }, + /** + * @description 续费专业版 + */ + renew_pro: function () { + var config = {name: 'Pro', pid: '100000058', limit: 'pro', renew: true}; + if (!bt.get_cookie('serial_no')) delete config.renew; + bt.soft.product_pay_view(config); + }, // updata_pro: function() { // bt.pub.get_user_info(function(rdata) { // if (!rdata.status) { @@ -4519,7 +4578,6 @@ bt.soft = { get_voucher_list: function(pid) { $("#couponlist").html("
                                  " + lan.public_backup.loading + "
                                  "); bt.soft.pro.get_voucher(pid, function(rdata) { - if (rdata != null && rdata.length > 0) { var con = ''; var len = rdata.length; @@ -4946,13 +5004,13 @@ bt.soft = { } return name }, - install: function(name,that) { + install: function (name, that) { var _this = this; - if(bt.soft.is_install){ - layer.msg('Installing other software, please operate later!',{icon:0}); - return false; - } - _this.get_soft_find(name, function(rdata) { + if (bt.soft.is_install) { + layer.msg('Installing other software, please operate later!', {icon: 0}); + return false; + } + _this.get_soft_find(name, function (rdata) { var arrs = ['apache', 'nginx', 'mysql']; if ($.inArray(name, arrs) >= 0 || name.indexOf('php-') >= 0) { var SelectVersion = '', shtml = name; @@ -4989,10 +5047,15 @@ bt.soft = {
                                  \
                                  \
                                  \ - \ + \ \
                                  \ -
                                  " +
                                  ", + success: function ($layer, index) { + $layer.find('.btn-close').click(function () { + layer.close(index); + }); + } }); $('.fangshi input').click(function() { @@ -5044,7 +5107,7 @@ bt.soft = { var info = $("#SelectVersion").val().toLowerCase(); name = info.split(" ")[0]; version = info.split(" ")[1]; - _this.install_soft(rdata, version,0,that); + _this.install_soft(rdata, version, 0, that); }); } else { _this.install_soft(rdata, rdata.versions[0].m_version,0,that); @@ -5148,21 +5211,39 @@ bt.soft = { // }) // }) // }, - install_soft: function (item, version, type,that) { //安装单版本 + install_soft: function (item, version, type, that) { //安装单版本 if (type == undefined) type = 0; var loadT = ''; - item.title = bt.replace_all(item.title,'-' + version,''); - layer.confirm(item.type!=5?lan.soft.lib_insatll_confirm.replace('{1}',item.title):lan.get('install_confirm',[item.title,version]),{ title:item.type!=5?lan.soft.lib_install:lan.soft.install_title,icon:0,closeBtn:2},function(){ - layer.closeAll(); - bt.soft.show_speed_window({title:'Installing '+ item.title +', please wait...',msg:lan.soft.lib_install_the,soft:item,event:that},function(){ - if(item.type == 10) loadT = layer.msg('Getting third party installation information, please wait', { icon: 16, time: 0, shade: [0.3, '#000'] }); - bt.send('install_plugin', 'plugin/install_plugin', { sName: item.name, version: version, type: type }, function (rdata) { - if (rdata.size) { - layer.close(loadT); - bt.soft.install_other(rdata,status); - return; - } - layer.close(bt.soft.loadT); + item.title = bt.replace_all(item.title, '-' + version, ''); + layer.confirm(item.type != 5 ? lan.soft.lib_insatll_confirm.replace('{1}', item.title) : lan.get('install_confirm', [item.title, version]), { + btn: [lan.public.confirm, lan.public.close], + title: item.type != 5 ? lan.soft.lib_install : lan.soft.install_title, + icon: 0, + closeBtn: 2 + }, function () { + layer.closeAll(); + bt.soft.show_speed_window({ + title: 'Installing ' + item.title + ', please wait...', + msg: lan.soft.lib_install_the, + soft: item, + event: that + }, function () { + if (item.type == 10) loadT = layer.msg('Getting third party installation information, please wait', { + icon: 16, + time: 0, + shade: [0.3, '#000'] + }); + bt.send('install_plugin', 'plugin/install_plugin', { + sName: item.name, + version: version, + type: type + }, function (rdata) { + if (rdata.size) { + layer.close(loadT); + bt.soft.install_other(rdata, status); + return; + } + layer.close(bt.soft.loadT); bt.pub.get_task_count(function(rdata){ if(rdata > 0 && item.type === 5) messagebox(); }); @@ -5233,6 +5314,7 @@ bt.soft = { // }) bt.soft.show_speed_window({title:'Updating to [' + title+'-'+version+'.'+min_version+'],Please wait...',status:true,soft:{type:parseInt(type)}},function(){ bt.send('install_plugin', 'plugin/install_plugin', { sName: name, version: version, upgrade: version }, function (rdata) { + console.log(rdata); if (rdata.size) { _this.install_other(rdata) return; @@ -5431,8 +5513,9 @@ bt.database = { }, 100) }) }, - add_database: function(callback) { + add_database: function(cloudList, callback) { bt.data.database.data_add.list[2].items[0].value = bt.get_random(16); + bt.data.database.data_add.list[5].items[0].items = cloudList; bt.render_form(bt.data.database.data_add, function(rdata) { if (callback) callback(rdata); }); @@ -5445,9 +5528,11 @@ bt.database = { if (callback) callback(rdata); }) }, - sync_database: function(callback) { + sync_database: function(sid, callback) { var loadT = bt.load(lan.database.sync_the); - bt.send('SyncGetDatabases', 'database/SyncGetDatabases', {}, function(rdata) { + bt.send('SyncGetDatabases', 'database/SyncGetDatabases', { + sid: sid + }, function (rdata) { loadT.close(); bt.msg(rdata); if (callback) callback(rdata); @@ -5926,7 +6011,7 @@ bt.site = { }, get_site_error_logs: function (siteName, callback) { var loading = bt.load(); - bt.send('get_site_errlog', 'site/get_site_errlog', { + bt.send('get_site_err_log', 'site/get_site_err_log', { siteName: siteName }, function (rdata) { loading.close(); @@ -6364,8 +6449,8 @@ bt.data = { var loading = bt.load(); bt.send('SetupPassword', 'database/SetupPassword', rdata, function(rRet) { loading.close(); - bt.msg(rRet); - load.close(); + layer.msg(rRet.msg, { icon: rRet.status ? 1 : 2, shade: .3 }) + if (rRet.status) load.close(); }) }) ] @@ -6373,9 +6458,11 @@ bt.data = { data_add: { title: lan.database.add_title, area: '530px', - list: [{ + list: [ + { title: 'DBName', - items: [{ + items: [ + { name: 'name', placeholder: lan.public_backup.new_db_name, type: 'text', @@ -6397,26 +6484,52 @@ bt.data = { } ] }, - { title: lan.public_backup.user_name, name: 'db_user', placeholder: lan.public_backup.db_user, width: '65%' }, + { + name: 'db_user', + title: lan.public_backup.user_name, + placeholder: lan.public_backup.db_user, + width: '65%' + }, bt.form.item.password, - bt.form.item.data_access, { title: lan.public_backup.type, name: 'dtype', type: 'select', disabled: (bt.contains(bt.get_cookie('serverType'), 'nginx') || bt.contains(bt.get_cookie('serverType'), 'apache') ? true : false), items: [ - { title: 'MySQL', value: 'MySQL' }, - { title: 'SQLServer', value: 'SQLServer' } + { + title: 'MySQL', + value: 'MySQL' + }, + { + title: 'SQLServer', + value: 'SQLServer' + } ] }, - { html: '
                                  \ - Force SSL\ -
                                  \ - \ - \ -
                                  \ -
                                  ' }, + bt.form.item.data_access, + { + title: lan.public_backup.add_to, + items: [ + { + name: 'sid', + width: '65%', + type: 'select', + items: [] + } + ] + }, + { + html: '\ +
                                  \ + Force SSL\ +
                                  \ + \ + \ +
                                  \ +
                                  \ + ' + } ], btns: [ bt.form.btn.close(), @@ -6432,7 +6545,10 @@ bt.data = { bt.msg(rRet); }) }) - ] + ], + success: function () { + $('[name=sid]').after('' + lan.public.manage_cloud_server + ''); + } }, data_access: { title: lan.public_backup.set_db_permissions, @@ -6999,6 +7115,110 @@ var form_group = { }); } } + +bt.public = { + + // 设置目录配额 + modify_path_quota:function (data,callback) { + var loadT = bt.load(lan.public.modify_path_quota) + $.post('/project/quota/modify_path_quota',data,function (res) { + loadT.close() + if(callback) callback(res) + }) + }, + + // 设置mysql配额 + modify_mysql_quota:function (data,callback) { + var loadT = bt.load(lan.public.modify_mysql_quota) + $.post('/project/quota/modify_mysql_quota',data,function (res) { + loadT.close() + if(callback) callback(res) + }) + }, + + /** + * @description 获取quoto容量 + */ + + get_quota_config:function (type) { + return { + fid: 'quota', + title: lan.public.capacity, + width: 120, + template:function(row,index){ + var quota = row.quota; + if(!quota.size) return '' + lan.public.notConfigured + '' + var size = quota.size * 1024 * 1024; + var speed = ((quota.used / size) * 100).toFixed(2) + var quotaFull = false + if(quota.size > 0 && quota.used >= (size)) quotaFull = true; + return '
                                  '+ + '
                                  '+ + '
                                  ' + }, + event:function(row, index, ev){ + var quota = row.quota; + var size = quota.size * 1024 * 1024 + var usedList = bt.format_size(quota.used).split(' '); + var quotaFull = false + if(quota.size > 0 && quota.used >= (size)) quotaFull = true; + var types = { + site: lan.site.website, + ftp: lan.site.add_site.ftp, + database: lan.site.database + } + layer.open({ + type:1, + title: '[' + row.name + '] '+ types[type] + ' ' + lan.public.quotaCapacity, + area:'476px', + closeBtn:2, + btn: [lan.public.save, lan.public.cancel], + content:'
                                  '+ + '' + lan.public.currentUsedCapacity + ''+ + '
                                  ' + + ''+ (!quotaFull?(quota.size != 0?usedList[1]:'MB'):'') +''+ + '
                                  '+ + '' + lan.public.quotaCapacity + ''+ + '
                                  '+ + 'MB'+ + '
                                  '+ + '
                                  '+ + '
                                    '+ + '
                                  • ' + lan.public.capacityTips1 + '
                                  • '+ + '
                                  • ' + lan.public.capacityTips2 + '
                                  • '+ + '
                                  • ' + lan.public.capacityTips3 + '
                                  • '+ + '
                                  • ' + lan.public.capacityTips4 + '
                                  • '+ + '
                                  • ' + lan.public.capacityTips5 + '
                                  • '+ + '
                                  '+ + '
                                  ', + yes:function (indexs) { + var quota_size = $('[name="quota_size"]').val() + if(type === 'site' || type === 'ftp'){ + bt.public.modify_path_quota({data:JSON.stringify({size:quota_size,path:row.path})},function (res) { + if(res.status){ + bt.msg(res) + layer.close(indexs) + setTimeout(function () { location.reload() },200) + }else{ + layer.msg(res.msg,{ icon:res.status?1:2, area:'650px',time:0,shade:.3,closeBtn:2}) + } + }) + }else{ + bt.public.modify_mysql_quota({data:JSON.stringify({size:quota_size,db_name:row.name})},function (res) { + bt.msg(res) + if(res.status){ + layer.close(indexs) + setTimeout(function () { location.reload() },200) + } + }) + } + } + }) + } + } + } +} + //设置面板SSL function setPanelSSL(){ var loadT = layer.msg(lan.config.ssl_msg,{icon:16,time:0,shade: [0.3, '#000']}); @@ -7036,7 +7256,7 @@ function setPanelSSL(){ } }, { - html:'
                                  '+lan.config.ssl_open_ps_5+'

                                  ' + html: '
                                  ' + lan.config.ssl_open_ps_5 + '

                                  ' } ], diff --git a/BTPanel/static/js/site.js b/BTPanel/static/js/site.js index dec15246..fea919c3 100644 --- a/BTPanel/static/js/site.js +++ b/BTPanel/static/js/site.js @@ -1,58 +1,60 @@ - -$('#cutMode span').on('click',function(){ +$('#cutMode .tabs-item').on('click', function () { + var type = $(this).data('type'); var index = $(this).index(); - $(this).addClass('on').siblings().removeClass('on'); - $(this).parent().next().find('.tab-con-block').eq(index).removeClass('hide').siblings().addClass('hide'); - switch(index){ - case 0: + $(this).addClass('active').siblings().removeClass('active'); + $('#site_table_view').find('.tab-con-block').eq(index).removeClass('hide').siblings().addClass('hide'); + switch (type) { + case 'php': $('#bt_site_table').empty(); if (!isSetup) { - // layer.msg('Web server is not installed
                                  Install Nginx  |  Install Apache', { icon: 7, shade: [0.3, '#000'], time: 0 }); - $('#bt_site_table+.mask_layer').removeClass('hide').find('.prompt_description').html('Web server is not installed, Install Nginx  |  Install Apache'); + $('.site_table_view .mask_layer').removeClass('hide').find('.prompt_description').html('Web server is not installed,Install Nginx  |  Install Apache'); + } else { + $('.site_table_view .mask_layer').addClass('hide'); } site.php_table_view(); site.get_types(); break; - case 1: + case 'nodejs': $('#bt_node_table').empty(); - $.get('/plugin?action=getConfigHtml',{name: "nodejs"},function(res){ + $.get('/plugin?action=getConfigHtml', {name: "nodejs"}, function (res) { // if(typeof res !== 'string') $('.site_table_view .mask_layer').removeClass('hide').find('.prompt_description').html('Node version manager is not installed,Click install'); if (typeof res !== 'string') { $('#bt_node_table+.mask_layer').removeClass('hide').find('.prompt_description').html('Node version manager is not installed,Click install'); } else { $('#bt_node_table+.mask_layer').addClass('hide'); } - }) + }); site.node_porject_view(); break; } - bt.set_cookie('site_tab_status',index==0?'php':'nodejs') + bt.set_cookie('site_model', type); }) - +var site_table; var site = { - node:{ + node: { /** * @description 选择路径配置 * @return config {object} 选中文件配置 * */ - get_project_select_path:function(path){ + get_project_select_path: function (path) { var that = this; return { type: 'text', width: '320px', name: 'project_script', - value:path, - placeholder:'Please select the project startup file and enter the startup command. It cannot be empty', + value: path, + placeholder: 'Please select the project startup file and enter the startup command. It cannot be empty', icon: { type: 'glyphicon-folder-open', - select:'file', - event: function (ev){} + select: 'file', + event: function (ev) { + } } } }, - get_project_select:function(path){ + get_project_select: function (path) { var that = this; return { type: 'select', @@ -61,16 +63,16 @@ var site = { disabled: true, unit: '* Get the startup mode in package.json', placeholder: 'Select the project continue', - list:path?function(configs){ - that.get_project_script_list(path,configs[2],this) - }:[], - change:function(formData,elements,formConfig){ + list: path ? function (configs) { + that.get_project_script_list(path, configs[2], this) + } : [], + change: function (formData, elements, formConfig) { var project_script = $("[data-name=\'project_script\']"); - if(formData.project_script === ''){ - if($("#project_script_two").length === 0){ - project_script.parent().after('
                                  ') + if (formData.project_script === '') { + if ($("#project_script_two").length === 0) { + project_script.parent().after('
                                  ') } - }else{ + } else { project_script.parent().next().remove(); } } @@ -84,24 +86,24 @@ var site = { * @param formObject {object} 表单对象 * @return config {object} 选中文件配置 */ - get_project_script_list:function(path,form,formObject){ + get_project_script_list: function (path, form, formObject) { var that = this; - that.get_start_command({project_cwd: path},function(res){ + that.get_start_command({project_cwd: path}, function (res) { var arry = []; for (var resKey in res) { - arry.push({title: resKey + ' 【' + res[resKey] + '】', value:resKey }) + arry.push({title: resKey + ' 【' + res[resKey] + '】', value: resKey}) } - arry.push({title:'Custom command',value:''}) + arry.push({title: 'Custom command', value: ''}) form.group = that.get_project_select(path); form.group.list = arry; form.group.disabled = false; formObject.$replace_render_content(2) - if(arry.length === 1){ + if (arry.length === 1) { var project_script = $("[data-name=\'project_script\']"); // form.group.value = ''; - project_script.parent().after('
                                  ') + project_script.parent().after('
                                  ') } - },function(){ + }, function () { form.label = 'Start file/command'; form.group = that.get_project_select_path(path); formObject.$replace_render_content(2) @@ -114,18 +116,21 @@ var site = { * @description 获取Node版本列表 * @return {{dataFilter: (function(*): *[]), url: string}} */ - get_node_version_list:function(){ + get_node_version_list: function () { return { url: '/project/nodejs/get_nodejs_version', - dataFilter: function(res) { - if(res.length === 0){ + dataFilter: function (res) { + if (res.length === 0) { layer.closeAll(); - bt.msg({status:false,msg:'Please open [Node Version Manager], install at least 1 Node version to continue'}) + bt.msg({ + status: false, + msg: 'Please open [Node Version Manager], install at least 1 Node version to continue' + }) return; } var arry = []; for (var i = 0; i < res.length; i++) { - arry.push({title:res[i],value:res[i]}) + arry.push({title: res[i], value: res[i]}) } return arry; } @@ -150,7 +155,8 @@ var site = { readonly: true, icon: { type: 'glyphicon-folder-open', - event: function (ev) {}, + event: function (ev) { + }, callback: function (path) { var filename = path.split('/'); var project_script_config = this.config.form[2], @@ -161,7 +167,7 @@ var site = { project_script_config.group.disabled = false; this.$replace_render_content(1) this.$replace_render_content(6) - that.get_project_script_list(path,project_script_config,this) + that.get_project_script_list(path, project_script_config, this) } }, value: bt.get_cookie('sites_path') ? bt.get_cookie('sites_path') : '/www/wwwroot', @@ -174,15 +180,15 @@ var site = { name: 'project_name', width: '350px', placeholder: 'Please enter the name of the Node project', - input: function (formData,formElement,formConfig) { - var project_ps_config = formConfig.config.form[6]; + input: function (formData, formElement, formConfig) { + var project_ps_config = formConfig.config.form[6]; project_ps_config.group.value = formData.project_name; formConfig.$replace_render_content(6) } } }, { label: 'Run opt', - group:(function(){ + group: (function () { return that.get_project_select(config.path) }()) }, { @@ -200,8 +206,8 @@ var site = { type: 'select', name: 'run_user', width: '150px', - unit:'* No special requirements,choose www user', - list: [{title:'www',value:'www'},{title: 'root', value: 'root'}], + unit: '* No special requirements,choose www user', + list: [{title: 'www', value: 'www'}, {title: 'root', value: 'root'}], tips: 'sssss' } }, { @@ -222,7 +228,7 @@ var site = { name: 'project_ps', width: '420px', placeholder: 'Please enter project remarks', - value:config.ps, + value: config.ps, } }, { label: 'Domain name', @@ -235,10 +241,10 @@ var site = { style: {top: '10px', left: '15px'}, } } - },{ - group:{ - type:'help', - list:[ + }, { + group: { + type: 'help', + list: [ '[Run opt]: The scripts list in package.json is read by default, or you can select the [Custom Command] option to manually enter the start command', '[Custom start]: You can select the startup file or directly enter the startup command. Supported startup methods: npm/node/pm2/yarn', '[Port]:The wrong port will lead to access to 502, if you dont know the port, you can change to the correct port after starting', @@ -261,27 +267,27 @@ var site = { label: '', group: { type: 'button', - name:'saveNodeConfig', + name: 'saveNodeConfig', title: 'Save', - event: function (data,form,that) { - if(data.project_cwd === ''){ - bt.msg({status:false,msg:'The project directory cannot be empty'}) + event: function (data, form, that) { + if (data.project_cwd === '') { + bt.msg({status: false, msg: 'The project directory cannot be empty'}) return false } var project_script_two = $('[name="project_script_two"]'); - if(data.project_script === '' && project_script_two.length < 1 || project_script_two.length > 1 && project_script_two.val() === ''){ - bt.msg({status:false,msg:'Start file/command cannot be empty'}) + if (data.project_script === '' && project_script_two.length < 1 || project_script_two.length > 1 && project_script_two.val() === '') { + bt.msg({status: false, msg: 'Start file/command cannot be empty'}) return false } - if(data.port === ''){ - bt.msg({status:false,msg:'Project port cannot be empty'}) + if (data.port === '') { + bt.msg({status: false, msg: 'Project port cannot be empty'}) return false } - if(data.project_script === ''){ + if (data.project_script === '') { data.project_script = project_script_two.val() delete data.project_script_two } - config.callback(data,form,that) + config.callback(data, form, that) } } }]) @@ -293,72 +299,75 @@ var site = { * @description 添加node项目表单 * @returns {{form: 当前实例对象, close: function(): void}} */ - add_node_form:function(callback){ + add_node_form: function (callback) { var that = this; var add_node_project = bt_tools.open({ - title:"Add Node project", - area:'700px', - btn:['Confirm','Cancel'], - content:{ + title: "Add Node project", + area: '700px', + btn: ['Confirm', 'Cancel'], + content: { class: 'pd30', - form:(function(){ + form: (function () { return that.get_node_general_config({ - form:add_node_project + form: add_node_project }) })() }, - yes:function(form,indexs,layers){ + yes: function (form, indexs, layers) { var defaultParam = { - bind_extranet:0, - is_power_on:1, - max_memory_limit:4096, - project_env:'' + bind_extranet: 0, + is_power_on: 1, + max_memory_limit: 4096, + project_env: '' } - if(form.domains !== ''){ - var arry = form.domains.replace('\n','').split('\r'),newArry = [] + if (form.domains !== '') { + var arry = form.domains.replace('\n', '').split('\r'), newArry = [] for (var i = 0; i < arry.length; i++) { var item = arry[i]; - if(bt.check_domain(item)){ - newArry.push(item.indexOf(':') > -1?item:item+':80') - }else{ - bt.msg({status:false,msg:'['+ item +'] The format of the bound domain name is incorrect'}) + if (bt.check_domain(item)) { + newArry.push(item.indexOf(':') > -1 ? item : item + ':80') + } else { + bt.msg({ + status: false, + msg: '[' + item + '] The format of the bound domain name is incorrect' + }) break; } } defaultParam.bind_extranet = 1 defaultParam.domains = newArry } - if(form.project_name === ''){ - bt.msg({status:false,msg:'Project name cannot be empty'}) + if (form.project_name === '') { + bt.msg({status: false, msg: 'Project name cannot be empty'}) return false } - var project_script_two = $('[name="project_script_two"]'); - if(project_script_two.length && project_script_two.val() === ''){ - bt.msg({status:false,msg:'Please enter a custom startup command, it cannot be empty!'}); + var project_script_two = $('[name="project_script_two"]'); + if (project_script_two.length && project_script_two.val() === '') { + bt.msg({status: false, msg: 'Please enter a custom startup command, it cannot be empty!'}); return false } - if(form.port === ''){ - bt.msg({status:false,msg:'Project port cannot be empty'}) + if (form.port === '') { + bt.msg({status: false, msg: 'Project port cannot be empty'}) return false } - if(form.project_script === null){ - bt.msg({status:false,msg:'Please select the project directory to get the start command!'}) + if (form.project_script === null) { + bt.msg({status: false, msg: 'Please select the project directory to get the start command!'}) return false } - form = $.extend(form,defaultParam) - if(project_script_two.length){ + form = $.extend(form, defaultParam) + if (project_script_two.length) { form.project_script = project_script_two.val() delete form.project_script_two } var _command = null; - setTimeout(function(){ - if(_command < 0) return false; - _command = that.request_module_log_command({shell:'tail -f /www/server/panel/logs/npm-exec.log'}) - },500); - site.node.add_node_project(form,function(res){ - if(!res.status) _command = -1 - if(_command > 0) layer.close(_command) - if(callback) callback(res,indexs) + setTimeout(function () { + if (_command < 0) return false; + _command = that.request_module_log_command({shell: 'tail -f /www/server/panel/logs/npm-exec.log'}) + }, 500); + site.node.add_node_project(form, function (res) { + if (!res.status) _command = -1 + if (_command > 0) layer.close(_command) + if (callback) callback(res, indexs) }) } }) @@ -370,20 +379,20 @@ var site = { * @param param {object} 请求参数 * @param callback {function} 回调函数 */ - add_node_project:function(param,callback){ - this.http({create_project:false,verify:false},param,callback) + add_node_project: function (param, callback) { + this.http({create_project: false, verify: false}, param, callback) }, /** * @description 获取Node环境 * @param callback {function} 回调函数 */ - get_node_environment:function(callback){ + get_node_environment: function (callback) { bt_tools.send({ - url:'/project/nodejs/is_install_nodejs' - },function(res){ - if(callback) callback(res); - },{load:'Get the Node project environment'}) + url: '/project/nodejs/is_install_nodejs' + }, function (res) { + if (callback) callback(res); + }, {load: 'Get the Node project environment'}) }, /** @@ -391,8 +400,8 @@ var site = { * @param param {object} 请求参数 * @param callback {function} 回调函数 */ - modify_node_project:function(param,callback){ - this.http({modify_project: 'Modify Node project configuration'},param,callback) + modify_node_project: function (param, callback) { + this.http({modify_project: 'Modify Node project configuration'}, param, callback) }, /** @@ -400,16 +409,16 @@ var site = { * @param param {object} 请求参数 * @param callback {function} 回调函数 */ - remove_node_project:function(param,callback){ - this.http({remove_project: 'Delete Node project'},param,callback) + remove_node_project: function (param, callback) { + this.http({remove_project: 'Delete Node project'}, param, callback) }, /** * @description 获取node项目域名 * @param callback {function} 回调行数 */ - get_node_project_domain:function(callback){ - this.http({project_get_domain: 'Get the list of Node project domain names'},callback) + get_node_project_domain: function (callback) { + this.http({project_get_domain: 'Get the list of Node project domain names'}, callback) }, /** @@ -418,16 +427,16 @@ var site = { * @param callback {function} 成功回调行数 * @param callback1 {function} 错误回调行数 */ - get_start_command:function(params,callback,callback1){ - this.http({get_run_list: 'Getting project start command'},params,callback,callback1) + get_start_command: function (params, callback, callback1) { + this.http({get_run_list: 'Getting project start command'}, params, callback, callback1) }, /** * @description 添加Node项目域名 * @param param {object} 请求参数 * @param callback {function} 回调行数 */ - add_node_project_domain:function(param,callback){ - this.http({project_add_domain:false,verify:false},param,callback) + add_node_project_domain: function (param, callback) { + this.http({project_add_domain: false, verify: false}, param, callback) }, /** @@ -435,8 +444,8 @@ var site = { * @param param {object} 请求参数 * @param callback {function} 回调行数 */ - remove_node_project_domain:function(param,callback){ - this.http({project_remove_domain:'Delete the Node project domain name'},param,callback) + remove_node_project_domain: function (param, callback) { + this.http({project_remove_domain: 'Delete the Node project domain name'}, param, callback) }, /** @@ -444,8 +453,8 @@ var site = { * @param param {object} 请求参数 * @param callback {function} 回调行数 */ - start_node_project:function(param,callback){ - this.http({start_project:'Enable Node project'},param,callback) + start_node_project: function (param, callback) { + this.http({start_project: 'Enable Node project'}, param, callback) }, /** @@ -453,8 +462,8 @@ var site = { * @param param {object} 请求参数 * @param callback {function} 回调行数 */ - stop_node_project:function(param,callback){ - this.http({stop_project:'Stop the Node project'},param,callback) + stop_node_project: function (param, callback) { + this.http({stop_project: 'Stop the Node project'}, param, callback) }, /** @@ -462,8 +471,8 @@ var site = { * @param param {object} 请求参数 * @param callback {function} 回调行数 */ - restart_node_project:function(param,callback){ - this.http({restart_project:'Restart the Node project'},param,callback) + restart_node_project: function (param, callback) { + this.http({restart_project: 'Restart the Node project'}, param, callback) }, /** @@ -471,8 +480,8 @@ var site = { * @param param {object} 请求参数 * @param callback {function} 回调行数 */ - get_node_project_info:function(param,callback){ - this.http({get_project_info:'Get Node project information'},param,callback) + get_node_project_info: function (param, callback) { + this.http({get_project_info: 'Get Node project information'}, param, callback) }, /** @@ -480,24 +489,24 @@ var site = { * @param param {object} 请求参数 * @param callback {function} 回调行数 */ - bind_node_project_map:function(param,callback){ - this.http({bind_extranet:'Mapping',verify:false},param,callback) + bind_node_project_map: function (param, callback) { + this.http({bind_extranet: 'Mapping', verify: false}, param, callback) }, /** * @description 绑定外网映射 * @param param {object} 请求参数 * @param callback {function} 回调行数 */ - unbind_node_project_map:function(param,callback){ - this.http({unbind_extranet:'Unmapping',verify:false},param,callback) + unbind_node_project_map: function (param, callback) { + this.http({unbind_extranet: 'Unmapping', verify: false}, param, callback) }, /** * @description 安装node项目依赖 * @param param {object} 请求参数 * @param callback {function} 回调行数 */ - install_node_project_packages:function(param,callback){ - this.http({install_packages:false,verify:false},param,callback) + install_node_project_packages: function (param, callback) { + this.http({install_packages: false, verify: false}, param, callback) }, /** @@ -505,104 +514,107 @@ var site = { * @param param {object} 请求参数 * @param callback {function} 回调行数 */ - npm_install_node_module:function(param,callback){ - this.http({install_module:'Install Node module'},param,callback) + npm_install_node_module: function (param, callback) { + this.http({install_module: 'Install Node module'}, param, callback) }, /** * @description 更新指定模块 * @param param {object} 请求参数 * @param callback {function} 回调行数 */ - upgrade_node_module:function(param,callback){ - this.http({upgrade_module:'Update Node module'},param,callback) - },/** + upgrade_node_module: function (param, callback) { + this.http({upgrade_module: 'Update Node module'}, param, callback) + }, /** * @description 删除指定模块 * @param param {object} 请求参数 * @param callback {function} 回调行数 */ - uninstall_node_module:function(param,callback){ - this.http({uninstall_module:'Uninstall the Node module'},param,callback) + uninstall_node_module: function (param, callback) { + this.http({uninstall_module: 'Uninstall the Node module'}, param, callback) }, /** * @description 模拟点击 */ - simulated_click:function(num){ - $('.bt-w-menu p:eq('+ num +')').click(); + simulated_click: function (num) { + $('.bt-w-menu p:eq(' + num + ')').click(); }, /** * @description 获取Node项目信息 * @param row {object} 当前行,项目信息 */ - set_node_project_view:function(row){ + set_node_project_view: function (row) { var that = this; bt.open({ - type:1, - title:'Node project management-['+ row.name +'], add time ['+ row.addtime +']', - skin:'node_project_dialog', - area:['800px','720px'], - content:'
                                  ' + - ''+ - '
                                  ' + - '
                                  '+ - '
                                  Please turn on mapping to viewing the configuration information
                                  '+ + type: 1, + title: 'Node project management-[' + row.name + '], add time [' + row.addtime + ']', + skin: 'node_project_dialog', + area: ['800px', '700px'], + content: '
                                  ' + + '' + + '
                                  ' + + '
                                  ' + + '
                                  Please turn on mapping to viewing the configuration information
                                  ' + '
                                  ', - btn:false, - success:function(layers){ + btn: false, + success: function (layers) { var $layers = $(layers), $content = $layers.find('#webedit-con'); + function reander_tab_list(config) { for (var i = 0; i < config.list.length; i++) { - var item = config.list[i],tab = $('

                                  ' + item.title + '

                                  '); + var item = config.list[i], + tab = $('

                                  ' + item.title + '

                                  '); $(config.el).append(tab); - (function (i,item) { - tab.on('click',function(ev){ + (function (i, item) { + tab.on('click', function (ev) { $('.mask_module').addClass('hide'); $(this).addClass('bgw').siblings().removeClass('bgw'); - if($(this).hasClass('bgw')) { - that.get_node_project_info({project_name:row.name},function(res){ - config.list[i].event.call(that,$content,res,ev) + if ($(this).hasClass('bgw')) { + that.get_node_project_info({project_name: row.name}, function (res) { + config.list[i].event.call(that, $content, res, ev) }) } }) - if(item.active) tab.click() - }(i,item)) + if (item.active) tab.click() + }(i, item)) } } + reander_tab_list({ - el:$layers.find('.bt-w-menu'), - list:[{ - title:'Project config', - active:true, - event:that.reander_node_project_config - },{ - title:'Domain', - event:that.reander_node_domain_manage - },{ - title:'Mapping', - event:that.reander_node_project_map, - },{ - title:'URL rewrite', - event:that.reander_node_project_rewrite - },{ - title:'Config file', - event:that.reander_node_file_config - },{ - title:'SSL', - event:that.reander_node_project_ssl - },{ - title:'Load status', - event:that.reander_node_service_condition - },{ - title:'service status', - event:that.reander_node_service_status - },{ - title:'Module', - event:that.reander_node_project_module - },{ - title:'Project log', - event:that.reander_node_project_log - },{ - title:'Website log', - event:that.reander_node_site_log + el: $layers.find('.bt-w-menu'), + list: [{ + title: 'Project config', + active: true, + event: that.reander_node_project_config + }, { + title: 'Domain', + event: that.reander_node_domain_manage + }, { + title: 'Mapping', + event: that.reander_node_project_map, + }, { + title: 'URL rewrite', + event: that.reander_node_project_rewrite + }, { + title: 'Config file', + event: that.reander_node_file_config + }, { + title: 'SSL', + event: that.reander_node_project_ssl + }, { + title: 'Load status', + event: that.reander_node_service_condition + }, { + title: 'service status', + event: that.reander_node_service_status + }, { + title: 'Module', + event: that.reander_node_project_module + }, { + title: 'Project log', + event: that.reander_node_project_log + }, { + title: 'Website log', + event: that.reander_node_site_log }] }) @@ -616,78 +628,84 @@ var site = { * @param row {object} 当前项目数据 * @param that {object} 当前node项目对象 */ - reander_node_project_config:function(el,rows){ - var row = $.extend(true,{},rows); - var that = this,edit_node_project = bt_tools.form({ - el:'#webedit-con', + reander_node_project_config: function (el, rows) { + var row = $.extend(true, {}, rows); + var that = this, edit_node_project = bt_tools.form({ + el: '#webedit-con', data: row.project_config, class: 'ptb10', - form:(function(){ + form: (function () { var fromConfig = that.get_node_general_config({ - form:edit_node_project, - path:row.path, - ps:row.ps, - callback:function(data,form,formNew){ - data['is_power_on'] = data['is_power_on']?1:0; - var project_script_two = $('[name="project_script_two"]'); - if(project_script_two.length && project_script_two.val() === ''){ - bt.msg({status:false,msg:'Please enter a custom startup command, it cannot be empty!'}); + form: edit_node_project, + path: row.path, + ps: row.ps, + callback: function (data, form, formNew) { + data['is_power_on'] = data['is_power_on'] ? 1 : 0; + var project_script_two = $('[name="project_script_two"]'); + if (project_script_two.length && project_script_two.val() === '') { + bt.msg({ + status: false, + msg: 'Please enter a custom startup command, it cannot be empty!' + }); return false } - if(form.port === ''){ - bt.msg({status:false,msg:'Project port cannot be empty'}) + if (form.port === '') { + bt.msg({status: false, msg: 'Project port cannot be empty'}) return false } - if(form.project_script === null){ - bt.msg({status:false,msg:'Please select the project directory to get the start command!'}) + if (form.project_script === null) { + bt.msg({ + status: false, + msg: 'Please select the project directory to get the start command!' + }) return false } - site.node.modify_node_project(data,function(res){ - if(res.status){ - row['project_config'] = $.extend(row,data); + site.node.modify_node_project(data, function (res) { + if (res.status) { + row['project_config'] = $.extend(row, data); row['path'] = data.project_script; row['ps'] = data.ps; } - bt.msg({status:res.status, msg:res.data}) + bt.msg({status: res.status, msg: res.data}) site.node.simulated_click(0) }) } }) - setTimeout(function(){ - var is_existence = false,list = fromConfig[2].group.list; + setTimeout(function () { + var is_existence = false, list = fromConfig[2].group.list; for (var i = 0; i < list.length; i++) { var item = list[i]; - if(item.value === rows.project_config.project_script){ + if (item.value === rows.project_config.project_script) { is_existence = true; break; } } - if(!is_existence && list.length > 1){ - $('[data-name="project_script"] li:eq('+ (list.length-1) + ')').click() + if (!is_existence && list.length > 1) { + $('[data-name="project_script"] li:eq(' + (list.length - 1) + ')').click() $('[name="project_script_two"]').val(rows.project_config.project_script) } - if(list.length === 1){ + if (list.length === 1) { $('[data-name="project_script"] li:eq(0)').click() $('[name="project_script_two"]').val(rows.project_config.project_script) } - },250) + }, 250) fromConfig[1].group.disabled = true; - fromConfig[fromConfig.length-3].hide = true; - fromConfig[fromConfig.length-3].group.disabled = true; + fromConfig[fromConfig.length - 3].hide = true; + fromConfig[fromConfig.length - 3].group.disabled = true; return fromConfig })() }) - setTimeout(function(){ + setTimeout(function () { $(el).append('
                                    ' + - '
                                  • [Run opt]: The scripts list in package.json is read by default, or you can select the [Custom Command] option to manually enter the start command
                                  • ' + - '
                                  • [Custom command]: You can select the startup file or directly enter the startup command. Supported startup methods: npm/node/pm2/yarn
                                  • ' + - '
                                  • [Port]:The wrong port will lead to access to 502, if you don’t know the port, you can fill it out at will, and then change to the correct port after starting the project
                                  • ' + - '
                                  • [User]:For security reasons, the www user is used by default to run, and root user running may bring security risks
                                  • ' + - '
                                  ') - if(!row.listen_ok) $(el).find('input[name="port"]').parent().after('
                                  The project port may be wrong, it is detected that the current project listens to the following ports[ '+ row.listen.join('/') +' ]
                                  '); + '
                                • [Run opt]: The scripts list in package.json is read by default, or you can select the [Custom Command] option to manually enter the start command
                                • ' + + '
                                • [Custom command]: You can select the startup file or directly enter the startup command. Supported startup methods: npm/node/pm2/yarn
                                • ' + + '
                                • [Port]:The wrong port will lead to access to 502, if you don’t know the port, you can fill it out at will, and then change to the correct port after starting the project
                                • ' + + '
                                • [User]:For security reasons, the www user is used by default to run, and root user running may bring security risks
                                • ' + + '') + if (!row.listen_ok) $(el).find('input[name="port"]').parent().after('
                                  The project port may be wrong, it is detected that the current project listens to the following ports[ ' + row.listen.join('/') + ' ]
                                  '); - },100) + }, 100) }, /** @@ -695,7 +713,7 @@ var site = { * @param el {object} 当前element节点 * @param row {object} 当前项目数据 */ - reander_node_service_status:function(el,row){ + reander_node_service_status: function (el, row) { var arry = [ {title: 'Start', event: this.start_node_project}, {title: 'Stop', event: this.stop_node_project}, @@ -703,34 +721,36 @@ var site = { ], that = this, html = $('

                                  '); - function reander_service(status){ - var status_info = status?['Start','#20a53a','play']:['Stop','red','pause']; - return 'Status: ' + status_info[0] + '' + + function reander_service(status) { + var status_info = status ? ['Start', '#20a53a', 'play'] : ['Stop', 'red', 'pause']; + return 'Status: ' + status_info[0] + '' } + html.find('.status').html(reander_service(row.run)) el.html(html) for (var i = 0; i < arry.length; i++) { var item = arry[i], btn = $(''); - (function (btn, item,indexs) { + (function (btn, item, indexs) { !(row.run && indexs === 0) || btn.addClass('hide'); - !(!row.run && indexs === 1 ) || btn.addClass('hide'); + !(!row.run && indexs === 1) || btn.addClass('hide'); btn.on('click', function () { bt.confirm({ - title:item.title +'Project-['+ row.name +']', - msg:'Are you sure you want the'+ item.title +'item,'+ (row.run?'The project may be affected,':'') +'continue?' - },function(index){ + title: item.title + 'Project-[' + row.name + ']', + msg: 'Are you sure you want the' + item.title + 'item,' + (row.run ? 'The project may be affected,' : '') + 'continue?' + }, function (index) { layer.close(index) - item.event.call(that, {project_name:row.name},function(res){ - row.run = (indexs === 0?true:(indexs === 1?false:row.run)) + item.event.call(that, {project_name: row.name}, function (res) { + row.run = (indexs === 0 ? true : (indexs === 1 ? false : row.run)) html.find('.status').html(reander_service(row.run)) $('.sfm-opt button').eq(0).addClass('hide'); $('.sfm-opt button').eq(1).addClass('hide'); - $('.sfm-opt button').eq(row.run?1:0).removeClass('hide'); - bt.msg({status:res.status,msg:res.data || res.error_msg}) + $('.sfm-opt button').eq(row.run ? 1 : 0).removeClass('hide'); + bt.msg({status: res.status, msg: res.data || res.error_msg}) }) }) }).text(item.title) - })(btn, item,i) + })(btn, item, i) el.find('.sfm-opt').append(btn) } }, @@ -741,25 +761,30 @@ var site = { * @param el {object} 当前element节点 * @param row {object} 当前项目数据 */ - reander_node_domain_manage:function(el,row){ + reander_node_domain_manage: function (el, row) { var that = this, list = [{ class: 'mb0', items: [ - { name: 'nodedomain', width: '340px', type: 'textarea', placeholder: 'Please enter the domain name to be mapped, this option can be empty
                                  One domain name per line, default port 80
                                  How to add wildcard domain names *.domain.com
                                  Specify the port used www.domain.com:88' }, + { + name: 'nodedomain', + width: '340px', + type: 'textarea', + placeholder: 'Please enter the domain name to be mapped, this option can be empty
                                  One domain name per line, default port 80
                                  How to add wildcard domain names *.domain.com
                                  Specify the port used www.domain.com:88' + }, { name: 'btn_node_submit_domain', text: 'Add', type: 'button', - callback: function(sdata) { + callback: function (sdata) { var arrs = sdata.nodedomain.split("\n"); var domins = []; for (var i = 0; i < arrs.length; i++) domins.push(arrs[i]); - that.add_node_project_domain({project_name:row.name,domains:domins}, function(res) { - bt.msg({status:res.status,msg:res.data || res.error_msg}); - if(res.status){ + that.add_node_project_domain({project_name: row.name, domains: domins}, function (res) { + bt.msg({status: res.status, msg: res.data || res.error_msg}); + if (res.status) { $('[name=nodedomain]').val(''); - $('.placeholder').css('display','block') + $('.placeholder').css('display', 'block') project_domian.$refresh_table_list(true); } }) @@ -767,57 +792,72 @@ var site = { } ] }] - var _form_data = bt.render_form_line(list[0]),loadT = null,placeholder = null; - el.html(_form_data.html+'
                                  '); + var _form_data = bt.render_form_line(list[0]), loadT = null, placeholder = null; + el.html(_form_data.html + '
                                  '); bt.render_clicks(_form_data.clicks); // domain样式 $('.btn_node_submit_domain').addClass('pull-right').css("margin", "30px 35px 0 0"); - $('textarea[name=nodedomain]').css('height','120px') + $('textarea[name=nodedomain]').css('height', '120px') placeholder = $(".placeholder"); - placeholder.click(function() { + placeholder.click(function () { $(this).hide(); $('.nodedomain').focus(); - }).css({ 'width': '340px', 'heigth': '120px', 'left': '0px', 'top': '0px', 'padding-top': '10px', 'padding-left': '15px' }) - $('.nodedomain').focus(function() { + }).css({ + 'width': '340px', + 'heigth': '120px', + 'left': '0px', + 'top': '0px', + 'padding-top': '10px', + 'padding-left': '15px' + }) + $('.nodedomain').focus(function () { placeholder.hide(); - loadT = layer.tips(placeholder.html(), $(this), { tips: [1, '#20a53a'], time: 0, area: $(this).width() }); - }).blur(function() { + loadT = layer.tips(placeholder.html(), $(this), {tips: [1, '#20a53a'], time: 0, area: $(this).width()}); + }).blur(function () { if ($(this).val().length == 0) placeholder.show(); layer.close(loadT); }); var project_domian = bt_tools.table({ - el:'#project_domian_list', - url:'/project/nodejs/project_get_domain', + el: '#project_domian_list', + url: '/project/nodejs/project_get_domain', default: "No domain name list yet", - param:{project_name:row.name}, - height:375, - beforeRequest:function(params){ - if(params.hasOwnProperty('data') && typeof params.data === 'string') return params - return {data:JSON.stringify(params)} + param: {project_name: row.name}, + height: 375, + beforeRequest: function (params) { + if (params.hasOwnProperty('data') && typeof params.data === 'string') return params + return {data: JSON.stringify(params)} }, - column:[{ type: 'checkbox', class: '', width: 20 }, { + column: [{type: 'checkbox', class: '', width: 20}, { fid: 'name', title: 'Domain Name', type: 'text', - template: function(row) { + template: function (row) { return '' + row.name + ''; } - },{ + }, { fid: 'port', title: 'Port', type: 'text' - },{ - title:'OPT', - type:'group', - width:'100px', + }, { + title: 'OPT', + type: 'group', + width: '100px', align: 'right', - group:[{ + group: [{ title: 'Del', - event: function (rowc,index,ev,key,rthat) { - that.remove_node_project_domain({project_name:row.name,domain:(rowc.name+':'+rowc.port)},function(res){ - bt.msg({status:res.status,msg:res.data || res.error_msg}) - rthat.$refresh_table_list(true); - }) + event: function (rowc, index, ev, key, rthat) { + bt.confirm({ + title: 'Delete domain [ ' + row.name + ' ]', + msg: lan.site.domain_del_confirm + }, function () { + that.remove_node_project_domain({ + project_name: row.name, + domain: (rowc.name + ':' + rowc.port) + }, function (res) { + bt.msg({status: res.status, msg: res.data || res.error_msg}) + rthat.$refresh_table_list(true); + }); + }); } }] }], @@ -832,7 +872,12 @@ var site = { load: true, url: '/project/nodejs/project_remove_domain', param: function (crow) { - return {data:JSON.stringify({project_name: row.name,domain: (crow.name+':'+crow.port)})} + return { + data: JSON.stringify({ + project_name: row.name, + domain: (crow.name + ':' + crow.port) + }) + } }, callback: function (that) { // 手动执行,data参数包含所有选中的站点 bt.show_confirm("Delete domain names in bulk", "Delete the selected domain name at the same time, do you want to continue?", function () { @@ -841,7 +886,7 @@ var site = { var html = '' for (var i = 0; i < list.length; i++) { var item = list[i]; - html += '' + item.name + '
                                  ' + (item.request.status?'Success':'Fail') + '
                                  '; + html += '' + item.name + '
                                  ' + (item.request.status ? 'Success' : 'Fail') + '
                                  '; } project_domian.$batch_success_table({ title: 'Batch deletion', @@ -855,14 +900,14 @@ var site = { }] }] }) - setTimeout(function(){ + setTimeout(function () { $(el).append('
                                    ' + - '
                                  • If yours is an HTTP project and needs to be mapped, please bind at least one domain name
                                  • ' + - '
                                  • It is recommended that all domain names use the default port 80
                                  • ' + + '
                                  • If yours is an HTTP project and needs to be mapped, please bind at least one domain name
                                  • ' + + '
                                  • It is recommended that all domain names use the default port 80
                                  • ' + '
                                  ') - },100) + }, 100) }, /** @@ -870,7 +915,7 @@ var site = { * @param el {object} 当前element节点 * @param row {object} 当前项目数据 */ - reander_node_project_map:function(el,row){ + reander_node_project_map: function (el, row) { var that = this; el.html('
                                  ' + ' Mapping' + @@ -879,25 +924,26 @@ var site = { ' ' + '
                                  ' + '
                                  • If your project is an HTTP project and you need to access the Internet through 80443, please use the mapping
                                  • Before using the mapping, please add at least 1 domain name in [Domain Name Management]
                                  ') - $('#node_project_map').attr('checked',row['project_config']['bind_extranet']?true:false) - $('[name=node_project_map]').click(function(){ - var _check = $('#node_project_map').prop('checked'),param = {project_name:row.name}; - if(!_check) param['domains'] = row['project_config']['domains'] - layer.confirm((!_check?'Enable':'Disable')+' mapping!,do you want to continue?',{ - title:'Mapping', - icon:0, - closeBtn:2, - cancel:function(){ - $('#node_project_map').attr('checked',_check) + $('#node_project_map').attr('checked', row['project_config']['bind_extranet'] ? true : false) + $('[name=node_project_map]').click(function () { + var _check = $('#node_project_map').prop('checked'), param = {project_name: row.name}; + if (!_check) param['domains'] = row['project_config']['domains'] + layer.confirm((!_check ? 'Enable' : 'Disable') + ' mapping!,do you want to continue?', { + btn: ['Confirm', 'Cancel'], + title: 'Mapping', + icon: 0, + closeBtn: 2, + cancel: function () { + $('#node_project_map').attr('checked', _check) } - },function(){ - that[_check?'unbind_node_project_map':'bind_node_project_map'](param,function(res){ - if(!res.status) $('#node_project_map').attr('checked',_check) - bt.msg({status:res.status,msg:typeof res.data != "string"?res.error_msg:res.data}) - row['project_config']['bind_extranet'] = _check?0:1 + }, function () { + that[_check ? 'unbind_node_project_map' : 'bind_node_project_map'](param, function (res) { + if (!res.status) $('#node_project_map').attr('checked', _check) + bt.msg({status: res.status, msg: typeof res.data != "string" ? res.error_msg : res.data}) + row['project_config']['bind_extranet'] = _check ? 0 : 1 }) - },function(){ - $('#node_project_map').attr('checked',_check) + }, function () { + $('#node_project_map').attr('checked', _check) }) }) }, @@ -907,94 +953,97 @@ var site = { * @param el {object} 当前element节点 * @param row {object} 当前项目数据 */ - reander_node_project_module:function(el,row){ + reander_node_project_module: function (el, row) { var that = this; el.html('
                                  ' + '
                                  ' + '
                                  ' + - '
                                  '); + '
                                  '); var node_project_module_table = bt_tools.table({ - el:'#node_module_list', - url:'/project/nodejs/get_project_modules', + el: '#node_module_list', + url: '/project/nodejs/get_project_modules', default: "The module is not installed, click one-click to install the project module, the default prompt when the data is empty", - param:{project_name:row.name,project_cwd:row.path}, - height:'576px', - load:'Retrieving module list, please wait...', - beforeRequest:function(params){ - if(params.hasOwnProperty('data') && typeof params.data === 'string') return params - return {data:JSON.stringify(params)} + param: {project_name: row.name, project_cwd: row.path}, + height: '576px', + load: 'Retrieving module list, please wait...', + beforeRequest: function (params) { + if (params.hasOwnProperty('data') && typeof params.data === 'string') return params + return {data: JSON.stringify(params)} }, - column:[{ + column: [{ fid: 'name', title: 'Module', type: 'text', - },{ + }, { fid: 'version', title: 'Ver', type: 'text', - width:'60px' - },{ + width: '60px' + }, { fid: 'license', title: 'License', type: 'text', - template:function(row){ - if(typeof row.license === "object") return ''+row.license.type+''; - return ''+row.license+'' + template: function (row) { + if (typeof row.license === "object") return '' + row.license.type + ''; + return '' + row.license + '' } - },{ + }, { fid: 'description', title: 'Description', - width:235, + width: 235, type: 'text', - template:function(row){ - return ''+row.description + '' + template: function (row) { + return '' + row.description + '' } - },{ - title:'OPT', - type:'group', - width:'125px', + }, { + title: 'OPT', + type: 'group', + width: '125px', align: 'right', - group:[{ + group: [{ title: 'Update', - event: function (rowc,index,ev,key,rthat) { - bt.show_confirm("Update module", "Updating the ["+rowc.name+"] module may affect the operation of the project, continue?", function () { - that.upgrade_node_module({project_name:row.name,mod_name:rowc.name},function(res){ - bt.msg({status:res.status,msg:res.data || res.error_msg}) + event: function (rowc, index, ev, key, rthat) { + bt.show_confirm("Update module", "Updating the [" + rowc.name + "] module may affect the operation of the project, continue?", function () { + that.upgrade_node_module({project_name: row.name, mod_name: rowc.name}, function (res) { + bt.msg({status: res.status, msg: res.data || res.error_msg}) rthat.$refresh_table_list(true); }); }); } - },{ + }, { title: 'Uninstall', - event: function (rowc,index,ev,key,rthat) { - bt.show_confirm("Uninstall the module", "Uninstalling the ["+rowc.name+"] module may affect the operation of the project, continue?", function () { - that.uninstall_node_module({project_name:row.name,mod_name:rowc.name},function(res){ - bt.msg({status:res.status,msg:res.data || res.error_msg}) + event: function (rowc, index, ev, key, rthat) { + bt.show_confirm("Uninstall the module", "Uninstalling the [" + rowc.name + "] module may affect the operation of the project, continue?", function () { + that.uninstall_node_module({ + project_name: row.name, + mod_name: rowc.name + }, function (res) { + bt.msg({status: res.status, msg: res.data || res.error_msg}) rthat.$refresh_table_list(true); }); }); } }] }], - success:function(config){ + success: function (config) { // 隐藏一键安装 - if(config.data.length > 0) $('.npm_install_node_config').addClass('hide'); + if (config.data.length > 0) $('.npm_install_node_config').addClass('hide'); } }) //安装模块 - $('.install_node_module').on('click',function(){ + $('.install_node_module').on('click', function () { var _mname = $('input[name=mname]').val(); - if(!_mname) return layer.msg('Please enter the module name and version',{icon:2}) - that.npm_install_node_module({project_name:row.name,mod_name:_mname},function(res){ - bt.msg({status:res.status,msg:res.data || res.error_msg}) + if (!_mname) return layer.msg('Please enter the module name and version', {icon: 2}) + that.npm_install_node_module({project_name: row.name, mod_name: _mname}, function (res) { + bt.msg({status: res.status, msg: res.data || res.error_msg}) node_project_module_table.$refresh_table_list(true); }) }) //一键安装项目模块 - $('.npm_install_node_config').on('click',function(){ - var _command = that.request_module_log_command({shell:'tail -f /www/server/panel/logs/npm-exec.log'}) - that.install_node_project_packages({project_name:row.name},function(res){ - if(res.status){ + $('.npm_install_node_config').on('click', function () { + var _command = that.request_module_log_command({shell: 'tail -f /www/server/panel/logs/npm-exec.log'}) + that.install_node_project_packages({project_name: row.name}, function (res) { + if (res.status) { node_project_module_table.$refresh_table_list(true); } layer.close(_command); @@ -1008,13 +1057,13 @@ var site = { * @param el {object} 当前element节点 * @param row {object} 当前项目数据 */ - reander_node_project_rewrite:function(el,row){ + reander_node_project_rewrite: function (el, row) { el.empty(); - if(row.project_config.bind_extranet === 0){ + if (row.project_config.bind_extranet === 0) { $('.mask_module').removeClass('hide').find('.node_mask_module_text:eq(1)').hide().prev().show() return false; } - site.edit.get_rewrite_list({name:'node_'+row.name},function(){ + site.edit.get_rewrite_list({name: 'node_' + row.name}, function () { $('.webedit-box .line:first').remove(); $('[name=btn_save_to]').remove(); $('.webedit-box .help-info-text li:first').remove(); @@ -1025,88 +1074,88 @@ var site = { * @param el {object} 当前element节点 * @param row {object} 当前项目数据 */ - reander_node_file_config:function(el,row){ + reander_node_file_config: function (el, row) { el.empty(); - if(row.project_config.bind_extranet === 0){ + if (row.project_config.bind_extranet === 0) { $('.mask_module').removeClass('hide').find('.node_mask_module_text:eq(1)').hide().prev().show() return false; } - site.edit.set_config({name:'node_'+row.name}) + site.edit.set_config({name: 'node_' + row.name}) }, /** * @description 渲染node项目使用情况 * @param el {object} 当前element节点 * @param row {object} 当前项目数据 */ - reander_node_service_condition:function(el,row){ - if(!row.run){ + reander_node_service_condition: function (el, row) { + if (!row.run) { el.html('').next().removeClass('hide') - if(el.next().find('.node_mask_module_text').length === 1) { + if (el.next().find('.node_mask_module_text').length === 1) { el.next().find('.node_mask_module_text').hide().parent().append('
                                  Please start the service first and try again,Set service status
                                  ') - }else{ + } else { el.next().find('.node_mask_module_text:eq(1)').show().prev().hide() } return false } el.html('
                                  PID
                                  ') - var _option = '',tabelCon = '' - for(var load in row.load_info){ - if(row.load_info.hasOwnProperty(load)){ - _option +=''; + var _option = '', tabelCon = '' + for (var load in row.load_info) { + if (row.load_info.hasOwnProperty(load)) { + _option += ''; } } var node_pid = $('[name=node_project_pid]'); node_pid.html(_option); - node_pid.change(function(){ - var _pid = $(this).val(),rdata = row['load_info'][_pid],fileBody = '',connectionsBody = ''; - for(var i=0;i'+itemi['path']+'' + - ''+itemi['mode']+'' + - ''+itemi['position']+'' + - ''+itemi['flags']+'' + - ''+itemi['fd']+'' + - ''; + '' + itemi['path'] + '' + + '' + itemi['mode'] + '' + + '' + itemi['position'] + '' + + '' + itemi['flags'] + '' + + '' + itemi['fd'] + '' + + ''; } - for(var k=0;k'+itemk['client_addr']+'' + - ''+itemk['client_rport']+'' + - ''+itemk['family']+'' + - ''+itemk['fd']+'' + - ''+itemk['local_addr']+'' + - ''+itemk['local_port']+'' + - ''+itemk['status']+'' + + '' + itemk['client_addr'] + '' + + '' + itemk['client_rport'] + '' + + '' + itemk['family'] + '' + + '' + itemk['fd'] + '' + + '' + itemk['local_addr'] + '' + + '' + itemk['local_port'] + '' + + '' + itemk['status'] + '' + '' } - // tabelCon = reand_table_config([ - // [{"名称":rdata.name},{"PID":rdata.pid},{"状态":rdata.status},{"父进程":rdata.ppid}], - // [{"用户":rdata.user},{"Socket":rdata.connects},{"CPU":rdata.cpu_percent},{"线程":rdata.threads}], - // [{"内存":rdata.user},{"io读":rdata.connects},{"io写":rdata.cpu_percent},{"启动时间":rdata.threads}], - // [{"启动命令":rdata.user}], - // ]) - // - // console.log(tabelCon) - // - // - // function reand_table_config(conifg){ - // var html = ''; - // for (var i = 0; i < conifg.length; i++) { - // var item = conifg[i]; - // html += ''; - // for (var j = 0; j < item; j++) { - // var items = config[j],name = Object.keys(items)[0]; - // console.log(items,name) - // html += ''+ name +''+ items[name] +'' - // } - // console.log(html) - // html += '' - // } - // return '
                                  '+ html +'
                                  '; - // } + // tabelCon = reand_table_config([ + // [{"名称":rdata.name},{"PID":rdata.pid},{"状态":rdata.status},{"父进程":rdata.ppid}], + // [{"用户":rdata.user},{"Socket":rdata.connects},{"CPU":rdata.cpu_percent},{"线程":rdata.threads}], + // [{"内存":rdata.user},{"io读":rdata.connects},{"io写":rdata.cpu_percent},{"启动时间":rdata.threads}], + // [{"启动命令":rdata.user}], + // ]) + // + // console.log(tabelCon) + // + // + // function reand_table_config(conifg){ + // var html = ''; + // for (var i = 0; i < conifg.length; i++) { + // var item = conifg[i]; + // html += ''; + // for (var j = 0; j < item; j++) { + // var items = config[j],name = Object.keys(items)[0]; + // console.log(items,name) + // html += ''+ name +''+ items[name] +'' + // } + // console.log(html) + // html += '' + // } + // return '
                                  '+ html +'
                                  '; + // } tabelCon = '
                                  ' + @@ -1119,16 +1168,16 @@ var site = { 'Start Time' + getLocalTime(rdata.create_time) + '' + '' + '' + - 'PID' + rdata.pid + '' + - 'PPID' + rdata.ppid + '' + - 'Thread' + rdata.threads + '' + - 'Socket' + rdata.connects + '' + + 'PID' + rdata.pid + '' + + 'PPID' + rdata.ppid + '' + + 'Thread' + rdata.threads + '' + + 'Socket' + rdata.connects + '' + '' + '' + - 'CPU' + rdata.cpu_percent + '%' + - 'RAM' + ToSize(rdata.memory_used) + '' + - 'Disk/R' + ToSize(rdata.io_read_bytes) + '' + - 'Dis/W' + ToSize(rdata.io_write_bytes) + '' + + 'CPU' + rdata.cpu_percent + '%' + + 'RAM' + ToSize(rdata.memory_used) + '' + + 'Disk/R' + ToSize(rdata.io_read_bytes) + '' + + 'Dis/W' + ToSize(rdata.io_write_bytes) + '' + '' + '' + @@ -1187,25 +1236,25 @@ var site = { * @param el {object} 当前element节点 * @param row {object} 当前项目数据 */ - reander_node_project_log:function(el,row){ + reander_node_project_log: function (el, row) { el.html('
                                  ') bt_tools.send({ - url:'/project/nodejs/get_project_log', - type:'GET', - data:{data:JSON.stringify({project_name: row.name})} - },function(res){ - $('#webedit-con .node_project_log').html('
                                  '+(typeof res == "object"?res.error_msg:res)+'
                                  ') - $('.command_output_pre').scrollTop($('.command_output_pre').prop('scrollHeight')) - },{load:'Get Node project log',verify:false}) + url: '/project/nodejs/get_project_log', + type: 'GET', + data: {data: JSON.stringify({project_name: row.name})} + }, function (res) { + $('#webedit-con .node_project_log').html('
                                  ' + (typeof res == "object" ? res.error_msg : res) + '
                                  ') + $('.command_output_pre').scrollTop($('.command_output_pre').prop('scrollHeight')) + }, {load: 'Get Node project log', verify: false}) }, - reander_node_site_log:function(el,row){ + reander_node_site_log: function (el, row) { el.empty() - if(row.project_config.bind_extranet === 0){ + if (row.project_config.bind_extranet === 0) { $('.mask_module').removeClass('hide').find('.node_mask_module_text:eq(1)').hide().prev().show() return false; } - site.edit.get_site_logs({name:row.name}) + site.edit.get_site_logs({name: row.name}) }, /** @@ -1213,30 +1262,34 @@ var site = { * @param el {object} 当前element节点 * @param row {object} 当前项目数据 */ - reander_node_project_ssl:function(el,row){ + reander_node_project_ssl: function (el, row) { el.empty(); - if(row.project_config.bind_extranet === 0){ + if (row.project_config.bind_extranet === 0) { $('.mask_module').removeClass('hide').find('.node_mask_module_text:eq(1)').hide().prev().show() return false; } - site.set_ssl({name:row.name,ele:el,id:row.id}); + site.set_ssl({name: row.name, ele: el, id: row.id}); site.ssl.reload(); }, /** * @description 请求模块日志终端 * @param config {object} 当前配置数据 */ - request_module_log_command:function(config){ + request_module_log_command: function (config) { var r_command = layer.open({ - title:config.name || 'Module is being installed, please wait...', + title: config.name || 'Module is being installed, please wait...', type: 1, closeBtn: 0, - area:['500px','342px'], - skin:config.class || 'module_commmand', + area: ['500px', '342px'], + skin: config.class || 'module_commmand', shadeClose: false, - content:'
                                  ', - success:function(){ - bt_tools.command_line_output({ el:'.site_module_command', shell:config.shell,area:config.area||['100%','300px']}) + content: '
                                  ', + success: function () { + bt_tools.command_line_output({ + el: '.site_module_command', + shell: config.shell, + area: config.area || ['100%', '300px'] + }) } }) return r_command; @@ -1249,7 +1302,7 @@ var site = { * @param callback {function} 成功回调函数 * @param callback1 {function} 错误调函数 */ - http:function(keyMethod,param,callback,callback1){ + http: function (keyMethod, param, callback, callback1) { var method = Object.keys(keyMethod), config = { url: '/project/nodejs/' + method[0], @@ -1259,36 +1312,42 @@ var site = { callback && callback(res) } if (callback1) { - bt_tools.send(config, success, callback1, {load: keyMethod[method[0]],verify:method[1]?keyMethod[method[1]]:true}) + bt_tools.send(config, success, callback1, { + load: keyMethod[method[0]], + verify: method[1] ? keyMethod[method[1]] : true + }) } else { - bt_tools.send(config,success,{load: keyMethod[method[0]],verify:method[1]?keyMethod[method[1]]:true}) + bt_tools.send(config, success, { + load: keyMethod[method[0]], + verify: method[1] ? keyMethod[method[1]] : true + }) } } }, - node_porject_view: function(){ + node_porject_view: function () { var node_table = bt_tools.table({ el: '#bt_node_table', url: '/project/nodejs/get_project_list', minWidth: '1000px', autoHeight: true, default: "The item list is empty", //数据为空时的默认提示\ - load:'Getting the list of Node projects, please wait...', - beforeRequest:function(params){ - if(params.hasOwnProperty('data') && typeof params.data === 'string') { + load: 'Getting the list of Node projects, please wait...', + beforeRequest: function (params) { + if (params.hasOwnProperty('data') && typeof params.data === 'string') { var oldParams = JSON.parse(params['data']) delete params['data'] - return {data:JSON.stringify($.extend(oldParams,params))} + return {data: JSON.stringify($.extend(oldParams, params))} } - return {data:JSON.stringify(params)} + return {data: JSON.stringify(params)} }, column: [ - { type: 'checkbox', class: '', width: 20 }, + {type: 'checkbox', class: '', width: 20}, { fid: 'name', title: 'Name', width: 85, type: 'link', - event: function(row, index, ev) { + event: function (row, index, ev) { site.node.set_node_project_view(row); }, template: function (row, index) { @@ -1307,58 +1366,58 @@ var site = { ] }, type: 'status', - event: function(row, index, ev, key, that) { + event: function (row, index, ev, key, that) { var status = row.run; bt.confirm({ - title:status?'Stop project':'Startup project', - msg:status?'After stopping the project, the project service will stop running, continue?':'Startup Node project ['+ row.name +'], continue operation?' - },function(index){ + title: status ? 'Stop project' : 'Startup project', + msg: status ? 'After stopping the project, the project service will stop running, continue?' : 'Startup Node project [' + row.name + '], continue operation?' + }, function (index) { layer.close(index) - site.node[status?'stop_node_project':'start_node_project']({project_name:row.name},function(res){ - bt.msg({status:res.status,msg:res.data || res.error_msg}) + site.node[status ? 'stop_node_project' : 'start_node_project']({project_name: row.name}, function (res) { + bt.msg({status: res.status, msg: res.data || res.error_msg}) that.$refresh_table_list(true); }) }) } }, { - fid:'pid', - title:'PID', - width:180, - type:'text', - template:function(row){ - if($.isEmptyObject(row['load_info'])) return '-' + fid: 'pid', + title: 'PID', + width: 180, + type: 'text', + template: function (row) { + if ($.isEmptyObject(row['load_info'])) return '-' var _id = [] - for(var i in row.load_info){ - if(row.load_info.hasOwnProperty(i)){ + for (var i in row.load_info) { + if (row.load_info.hasOwnProperty(i)) { _id.push(i) } } - return ''+_id.join(',')+'' + return '' + _id.join(',') + '' } }, { - title:'CPU', - type:'text', - template:function(row){ - if($.isEmptyObject(row['load_info'])) return '-' + title: 'CPU', + type: 'text', + template: function (row) { + if ($.isEmptyObject(row['load_info'])) return '-' var _cpu_total = 0; - for(var i in row.load_info){ + for (var i in row.load_info) { _cpu_total += row.load_info[i]['cpu_percent'] } - return ''+ _cpu_total.toFixed(2) +'%' + return '' + _cpu_total.toFixed(2) + '%' } }, { - title:'RAM', - type:'text', - template:function(row){ - if($.isEmptyObject(row['load_info'])) return '-' + title: 'RAM', + type: 'text', + template: function (row) { + if ($.isEmptyObject(row['load_info'])) return '-' var _cpu_total = 0; - for(var i in row.load_info){ - _cpu_total+=row.load_info[i]['memory_used'] + for (var i in row.load_info) { + _cpu_total += row.load_info[i]['memory_used'] } - return ''+bt.format_size(_cpu_total)+'' + return '' + bt.format_size(_cpu_total) + '' } }, { @@ -1366,7 +1425,7 @@ var site = { title: 'Root directory', tips: 'Open Directory', type: 'link', - event: function(row, index, ev) { + event: function (row, index, ev) { openPath(row.path); }, template: function (row, index) { @@ -1374,35 +1433,35 @@ var site = { } }, { - fid:'node_version', - title:'Node version', - type:'text', + fid: 'node_version', + title: 'Node version', + type: 'text', width: 102, - template:function(row){ - return ''+row['project_config']['nodejs_version']+'' + template: function (row) { + return '' + row['project_config']['nodejs_version'] + '' } - },{ + }, { fid: 'ps', title: 'Remark', type: 'input', - blur: function(row, index, ev, key, that) { + blur: function (row, index, ev, key, that) { if (row.ps == ev.target.value) return false; - bt.pub.set_data_ps({ id: row.id, table: 'sites', ps: ev.target.value }, function(res) { - bt_tools.msg(res, { is_dynamic: true }); + bt.pub.set_data_ps({id: row.id, table: 'sites', ps: ev.target.value}, function (res) { + bt_tools.msg(res, {is_dynamic: true}); }); }, - keyup: function(row, index, ev) { + keyup: function (row, index, ev) { if (ev.keyCode === 13) { $(this).blur(); } } - },{ + }, { fid: 'ssl', title: 'SSL', tips: 'Deployment certificate', width: 100, type: 'text', - template: function(row, index) { + template: function (row, index) { var _ssl = row.ssl, _info = '', _arry = [ @@ -1417,35 +1476,36 @@ var site = { return 'Exp in ' + Math.row.ssl.endtime + ' days'; } } - } catch (error) {} + } catch (error) { + } for (var i = 0; i < _arry.length; i++) { var item = _ssl[_arry[i][0]]; _info += _arry[i][1] + ':' + item + (_arry.length - 1 != i ? '\n' : ''); } return row.ssl === -1 ? 'Not Set' : 'Exp in ' + row.ssl.endtime + ' days'; }, - event: function(row) { + event: function (row) { site.node.set_node_project_view(row); - setTimeout(function() { + setTimeout(function () { $('.site-menu p:eq(5)').click(); }, 500); } - },{ + }, { title: 'OPT', type: 'group', width: 100, align: 'right', group: [{ title: 'Set', - event: function(row, index, ev, key, that) { + event: function (row, index, ev, key, that) { site.node.set_node_project_view(row); } }, { title: 'Del', - event: function(row, index, ev, key, that) { - bt.prompt_confirm('Delete item','You are deleting the Node project-['+ row.name +'], continue?',function(){ - site.node.remove_node_project({project_name:row.name},function(res){ - bt.msg({status:res.status,msg:res.data || res.error_msg}) + event: function (row, index, ev, key, that) { + bt.prompt_confirm('Delete item', 'You are deleting the Node project-[' + row.name + '], continue?', function () { + site.node.remove_node_project({project_name: row.name}, function (res) { + bt.msg({status: res.status, msg: res.data || res.error_msg}) node_table.$refresh_table_list(true); }) }) @@ -1453,8 +1513,8 @@ var site = { }] } ], - sortParam: function(data) { - return { 'order': data.name + ' ' + data.sort }; + sortParam: function (data) { + return {'order': data.name + ' ' + data.sort}; }, // 渲染完成 tootls: [{ // 按钮组 @@ -1463,19 +1523,22 @@ var site = { list: [{ title: 'Add Node project', active: true, - event: function(ev) { - site.node.add_node_form(function(res,index){ - if(res.status) { + event: function (ev) { + site.node.add_node_form(function (res, index) { + if (res.status) { layer.close(index) node_table.$refresh_table_list(true); } - bt.msg({status:res.status, msg: (!Array.isArray(res.data)?res.data:false) || res.error_msg}) + bt.msg({ + status: res.status, + msg: (!Array.isArray(res.data) ? res.data : false) || res.error_msg + }) }) } - },{ + }, { title: 'Node version manager', - event: function(ev) { - bt.soft.set_lib_config('nodejs','Node.js version manager') + event: function (ev) { + bt.soft.set_lib_config('nodejs', 'Node.js version manager') } }] }, { // 搜索内容 @@ -1493,21 +1556,25 @@ var site = { selectList: [{ title: "Delete item", url: '/project/nodejs/remove_project', - param:function(row){ + param: function (row) { return { - data:JSON.stringify({project_name:row.name}) + data: JSON.stringify({project_name: row.name}) } }, refresh: true, - callback: function(that) { - bt.prompt_confirm('Delete items in bulk','You are deleting the selected Node project. Continue?',function(){ - that.start_batch({}, function(list) { + callback: function (that) { + bt.prompt_confirm('Delete items in bulk', 'You are deleting the selected Node project. Continue?', function () { + that.start_batch({}, function (list) { var html = ''; for (var i = 0; i < list.length; i++) { var item = list[i]; - html += '' + item.name + '
                                  ' + (item.requests.status?item.requests.data:item.requests.error_msg) + '
                                  '; + html += '' + item.name + '
                                  ' + (item.requests.status ? item.requests.data : item.requests.error_msg) + '
                                  '; } - node_table.$batch_success_table({ title: 'Delete items in bulk', th: 'project name', html: html }); + node_table.$batch_success_table({ + title: 'Delete items in bulk', + th: 'project name', + html: html + }); node_table.$refresh_table_list(true); }); }) @@ -1528,52 +1595,64 @@ var site = { }] }); }, - php_table_view:function(){ + php_table_view: function () { $('#bt_site_table').empty(); - var site_table = bt_tools.table({ - el:'#bt_site_table', - url:'/data?action=getData', - cookiePrefix:'site_table', // cookie前缀,用于状态存储,如果不设置,着所有状态不存储, - param:{table:'sites'}, //参数 - minWidth:'1000px', - autoHeight:true, - default:"Site list is empty", // 数据为空时的默认提示 - beforeRequest:function(param){ + site_table = bt_tools.table({ + el: '#bt_site_table', + url: '/data?action=getData', + cookiePrefix: 'site_table', // cookie前缀,用于状态存储,如果不设置,着所有状态不存储, + param: {table: 'sites'}, //参数 + minWidth: '1000px', + autoHeight: true, + default: "Site list is empty", // 数据为空时的默认提示 + beforeRequest: function (param) { param.type = bt.get_cookie('site_type') || -1; return param; }, - column:[ - {type:'checkbox',class:'',width:20}, + column: [ + {type: 'checkbox', class: '', width: 20}, { fid: 'name', title: lan.site.site_name, sort: true, sortValue: 'asc', type: 'link', - event: function(row,index,ev) { - site.web_edit(row,true); + width: 130, + event: function (row, index, ev) { + site.web_edit(row, true); }, template: function (row, index) { - return ''; + return ''; } }, - {fid:'status',title:lan.site.status,sort:true,width:85,config:{icon:true,list:[['1',lan.site.running_text,'bt_success','glyphicon-play'],['0',lan.site.stopped,'bt_danger','glyphicon-pause']]},type:'status',event:function(row,index,ev,key,that){ - bt.site[parseInt(row.status)?'stop':'start'](row.id,row.name,function(res){ - if(res.status) that.$modify_row_data({status:parseInt(row.status)?'0':'1'}); - }); - }}, { - fid:'backup_count', - title:lan.site.backup, - width:80, - type:'link', - template:function(row,index){ - var backup = lan.site.backup_no,_class = "bt_warning"; - if (row.backup_count > 0) backup = lan.site.backup_yes,_class = "bt_success"; - return ''+ backup + (row.backup_count >0?('('+ row.backup_count +')'):'') +''; + fid: 'status', + title: lan.site.status, + sort: true, + width: 85, + config: { + icon: true, + list: [['1', lan.site.running_text, 'bt_success', 'glyphicon-play'], ['0', lan.site.stopped, 'bt_danger', 'glyphicon-pause']] }, - event:function(row,index){ - site.backup_site_view({id:row.id,name:row.name},site_table); + type: 'status', + event: function (row, index, ev, key, that) { + bt.site[parseInt(row.status) ? 'stop' : 'start'](row.id, row.name, function (res) { + if (res.status) that.$modify_row_data({status: parseInt(row.status) ? '0' : '1'}); + }); + } + }, + { + fid: 'backup_count', + title: lan.site.backup, + width: 80, + type: 'link', + template: function (row, index) { + var backup = lan.site.backup_no, _class = "bt_warning"; + if (row.backup_count > 0) backup = lan.site.backup_yes, _class = "bt_success"; + return '' + backup + (row.backup_count > 0 ? ('(' + row.backup_count + ')') : '') + ''; + }, + event: function (row, index) { + site.backup_site_view({id: row.id, name: row.name}, site_table); } }, { @@ -1581,22 +1660,23 @@ var site = { title: lan.site.root_dir, tips: 'Open path', type: 'link', - event: function (row,index,ev) { + event: function (row, index, ev) { openPath(row.path); }, template: function (row, index) { return ''; } }, + bt.public.get_quota_config('site'), { - fid:'edate', + fid: 'edate', title: lan.site.endtime, width: 115, class: 'set_site_edate', sort: true, type: 'link', - template: function (row,index) { - var _endtime = row.edate || row.endtime; + template: function (row, index) { + var _endtime = row.edate || row.endtime; if (_endtime === "0000-00-00") { return lan.site.web_end_time; } else { @@ -1607,75 +1687,116 @@ var site = { } } }, - event: function (row) {} + event: function (row) { + } }, //模拟点击误删 - {fid:'ps',title:lan.site.note,type:'input',blur:function(row,index,ev){ - if (row.ps == ev.target.value) return false; - bt.pub.set_data_ps({id:row.id,table:'sites',ps:ev.target.value},function(res){ - if(!res.status) layer.msg(res.msg,{status:2}); - }); - },keyup:function(row,index,ev){ - if(ev.keyCode === 13){ - $(this).blur(); - } - }}, - {fid:'php_version',title:'PHP',tips:'Selete php version',width:57,type:'link',template:function(row,index){ - if(row.php_version.indexOf('static') > -1) return row.php_version; - return row.php_version; - },event:function(row,index){ - site.web_edit(row); - setTimeout(function(){ - $('.site-menu p:eq(9)').click(); - },500); - }}, - {fid:'ssl',title:'SSL',tips:'Deployment certificate',width:140,type:'text',template:function(row,index){ - var _ssl = row.ssl,_info = '',_arry = [['issuer','Certificate'],['notAfter','Due date'],['notBefore','Application date'],['dns','Domain name']]; - try { - if (typeof row.ssl.endtime != 'undefined') { - if (row.ssl.endtime < 0) { - return 'Exp in ' + Math.row.ssl.endtime + ' days'; - } - } - } catch (error) {} - for (var i = 0; i < _arry.length; i++) { - var item = _ssl[_arry[i][0]]; - _info += _arry[i][1] + ':' + item + (_arry.length - 1 != i ? '\n' : ''); - } - return row.ssl === -1 ? 'Not Set' : 'Exp in ' + row.ssl.endtime + ' days'; - },event:function(row,index,ev,key,that){ - console.log(row,'111'); - site.web_edit(row); - setTimeout(function(){ - $('.site-menu p:eq(8)').click(); - },500); - }}, - {title:lan.site.operate,type:'group',width:119,align:'right',group:[ { - title:'WAF', - event:function(row,index,ev,key,that){ - site.site_waf(row.name); + fid: 'ps', title: lan.site.note, type: 'input', blur: function (row, index, ev) { + if (row.ps == ev.target.value) return false; + bt.pub.set_data_ps({id: row.id, table: 'sites', ps: ev.target.value}, function (res) { + if (!res.status) layer.msg(res.msg, {status: 2}); + }); + }, keyup: function (row, index, ev) { + if (ev.keyCode === 13) { + $(this).blur(); + } } }, { - title:lan.site.set, - event:function(row,index,ev,key,that){ - site.web_edit(row,true); + fid: 'php_version', + title: 'PHP', + tips: 'Selete php version', + width: 57, + type: 'link', + template: function (row, index) { + if (row.php_version.indexOf('static') > -1) return row.php_version; + return row.php_version; + }, + event: function (row, index) { + site.web_edit(row); + setTimeout(function () { + $('.site-menu p:eq(9)').click(); + }, 500); } - },{ - title:'Del', - event:function(row,index,ev,key,that){ - site.del_site(row.id,row.name,function(){ - that.$refresh_table_list(true); - }); + }, + { + fid: 'ssl', + title: 'SSL', + tips: 'Deployment certificate', + width: 110, + type: 'text', + template: function (row, index) { + var _ssl = row.ssl, _info = '', + _arry = [['issuer', 'Certificate'], ['notAfter', 'Due date'], ['notBefore', 'Application date'], ['dns', 'Domain name']]; + try { + if (typeof row.ssl.endtime != 'undefined') { + if (row.ssl.endtime < 0) { + return 'Exp in ' + Math.row.ssl.endtime + ' days'; + } + } + } catch (error) { + } + for (var i = 0; i < _arry.length; i++) { + var item = _ssl[_arry[i][0]]; + _info += _arry[i][1] + ':' + item + (_arry.length - 1 != i ? '\n' : ''); + } + return row.ssl === -1 ? 'Not Set' : 'Exp in ' + row.ssl.endtime + ' days'; + }, + event: function (row, index, ev, key, that) { + // console.log(row, '111'); + site.web_edit(row); + setTimeout(function () { + $('.site-menu p:eq(8)').click(); + }, 500); } - }]} + }, + { + fid:'attack', + title:'Attack', + width:80, + type:'text', + template:function(row,index){ + return ''+row.attack+'' + }, + event:function(row){ + site.web_edit(row); + setTimeout(function () { + $('.site-menu p:eq('+($('.site-menu p').length-1)+')').click(); + setTimeout(function(){ + $('#tabLogs span:eq(2)').click(); + },500) + }, 500); + } + }, + { + title: lan.site.operate, type: 'group', width: 140, align: 'right', group: [ + { + title: 'WAF', + event: function (row, index, ev, key, that) { + site.site_waf(row.name); + } + }, + { + title: lan.site.set, + event: function (row, index, ev, key, that) { + site.web_edit(row, true); + } + }, { + title: 'Del', + event: function (row, index, ev, key, that) { + site.del_site(row.id, row.name, function () { + that.$refresh_table_list(true); + }); + } + }] + } ], - sortParam:function(data){ - return {'order':data.name +' '+ data.sort}; + sortParam: function (data) { + return {'order': data.name + ' ' + data.sort}; }, // 表格渲染完成后 success: function (that) { - $('.event_edate_' + that.random).each(function(){ + $('.event_edate_' + that.random).each(function () { var $this = $(this); laydate.render({ elem: $this[0] //指定元素 @@ -1687,13 +1808,13 @@ var site = { , trigger: 'click' , btns: ['perpetual', 'confirm'] , theme: '#20a53a' - , ready:function(){ + , ready: function () { $this.click(); } , done: function (date) { var item = that.event_rows_model.rows; - bt.site.set_endtime(item.id, date,function(res){ - if(res.status){ + bt.site.set_endtime(item.id, date, function (res) { + if (res.status) { layer.msg(res.msg); return false; } @@ -1702,163 +1823,210 @@ var site = { } }); }); + if($('#bt_site_table table thead th:eq(9) a').length == 0){ + var Attack_tips = '
                                    \ +
                                  • Log analysis: Scan the logs(/www/wwwroot/.log) for requests with attack (types include:xss,sql,san,php)
                                  • \ +
                                  • Analyzed log data contains intercepted requests
                                  • \ +
                                  • By default, the last scan data is displayed (if not, please click log scan)
                                  • \ +
                                  • If the log file is too large, scanning may take a long time, please be patient
                                  • \ +
                                  • aaPanel WAF can effectively block such attacks
                                  • \ +
                                  ' + $('#bt_site_table table thead th:eq(9)>span').css({'width':'42px','display':'initial'}) //设置扫描th大小 + //追加tips并设置样式 + $('#bt_site_table table thead th:eq(9)').append($('?').css({'border-color':'#666','color':'#666'}).hover(function(){ + $(this).css({'border-color':'#fb7d00','color':'#fff'}) + layer.tips(Attack_tips, $(this), { time: 0, tips: [1, '#fff'],area: ['500px', '180px']}); + },function(){ + $(this).css({'border-color':'#666','color':'#666'}) + layer.closeAll('tips'); + })) + } }, // 渲染完成 - tootls:[{ // 按钮组 - type:'group', - positon:['left','top'], - list:[ - {title:'Add site',active:true, event:function(ev){ site.add_site(function(){ - site_table.$refresh_table_list(true) }); - bt.set_cookie('site_type','-1'); - }}, - {title:'Default Page',event:function(ev){ site.set_default_page() }}, - {title:'Default Website',event:function(ev){ site.set_default_site() }}, - {title:'PHP CLI version',event:function(ev){ site.get_cli_version()}} - ] - },{ // 搜索内容 - type:'search', - positon:['right','top'], - placeholder:'Domain or Remarks', - searchParam:'search', //搜索请求字段,默认为 search - value:'',// 当前内容,默认为空 - },{ // 批量操作 - type:'batch',//batch_btn - positon:['left','bottom'], - placeholder:'Select batch operation', - buttonValue:'Execute', - disabledSelectValue:'Select the website to execute!', - selectList:[ + tootls: [{ // 按钮组 + type: 'group', + positon: ['left', 'top'], + list: [ { - group:[{title:lan.site.enable_website,param:{status:1}},{title:'Disable website',param:{status:0}}], - url:'/site?action=set_site_status_multiple', - confirmVerify:false, //是否提示验证方式 - paramName:'sites_id', //列表参数名,可以为空 - paramId:'id', // 需要传入批量的id - theadName:'Name', + title: 'Add site', + active: true, + event: function (ev) { + site.add_site(function (res, param) { + var id = bt.get_cookie('site_type'); + if (param) { // 创建站点 + if (id != -1 && id != param.type_id) { + $('#php_cate_select .bt_select_list .item.active').click(); + } else { + site_table.$refresh_table_list(true); + } + } else { // 批量添加 + $('#php_cate_select .bt_select_list li[data-id="-1"]').click(); + } + }); + } + }, + { + title: 'Default Page', event: function (ev) { + site.set_default_page() + } + }, + { + title: 'Default Website', event: function (ev) { + site.set_default_site() + } + }, + { + title: 'PHP CLI version', event: function (ev) { + site.get_cli_version() + } + } + ] + }, { // 搜索内容 + type: 'search', + positon: ['right', 'top'], + placeholder: 'Domain or Remarks', + searchParam: 'search', //搜索请求字段,默认为 search + value: '',// 当前内容,默认为空 + }, { // 批量操作 + type: 'batch',//batch_btn + positon: ['left', 'bottom'], + placeholder: 'Select batch operation', + buttonValue: 'Execute', + disabledSelectValue: 'Select the website to execute!', + selectList: [ + { + group: [{title: lan.site.enable_website, param: {status: 1}}, { + title: 'Disable website', + param: {status: 0} + }], + url: '/site?action=set_site_status_multiple', + confirmVerify: false, //是否提示验证方式 + paramName: 'sites_id', //列表参数名,可以为空 + paramId: 'id', // 需要传入批量的id + theadName: 'Name', refresh: true - },{ - title:lan.site.backup_website, - url:'/site?action=ToBackup', - paramId:'id', - load:true, - theadName:'Name', + }, { + title: lan.site.backup_website, + url: '/site?action=ToBackup', + paramId: 'id', + load: true, + theadName: 'Name', refresh: true, - callback:function(that){ // 手动执行,data参数包含所有选中的站点 - that.start_batch({},function(list){ + callback: function (that) { // 手动执行,data参数包含所有选中的站点 + that.start_batch({}, function (list) { var html = ''; - for(var i=0;i'+ item.name +''+ item.request.msg +''; + html += '' + item.name + '' + item.request.msg + ''; } - site_table.$batch_success_table({title:'Batch backup',th:'Site name',html:html}); + site_table.$batch_success_table({title: 'Batch backup', th: 'Site name', html: html}); site_table.$refresh_table_list(true); }); } - },{ - title:lan.site.set_expired, - url:'/site?action=set_site_etime_multiple', - paramName:'sites_id', //列表参数名,可以为空 - paramId:'id', // 需要传入批量的id - theadName:'Name', + }, { + title: lan.site.set_expired, + url: '/site?action=set_site_etime_multiple', + paramName: 'sites_id', //列表参数名,可以为空 + paramId: 'id', // 需要传入批量的id + theadName: 'Name', refresh: true, - confirm:{ - title:'Batch set expired date', - content:'
                                  Expired date
                                  ', - success:function(){ + confirm: { + title: 'Batch set expired date', + content: '
                                  Expired date
                                  ', + success: function () { laydate.render({ elem: '#site_edate' - ,min: bt.format_data(new Date().getTime(),'yyyy-MM-dd') - ,max: '2099-12-31' - ,vlue: bt.get_date(365) - ,type: 'date' - ,format: 'yyyy-MM-dd' - ,trigger: 'click' - ,btns: ['perpetual','confirm'] - ,theme: '#20a53a' + , min: bt.format_data(new Date().getTime(), 'yyyy-MM-dd') + , max: '2099-12-31' + , vlue: bt.get_date(365) + , type: 'date' + , format: 'yyyy-MM-dd' + , trigger: 'click' + , btns: ['perpetual', 'confirm'] + , theme: '#20a53a' }); }, - yes:function(index,layers,request){ - var site_edate = $('#site_edate'),site_edate_val = site_edate.val(); - if(site_edate_val != ''){ - request({'edate':site_edate_val==='Forever'?'0000-00-00':site_edate_val}); - }else{ - layer.tips('Input expired date','#site_edate',{tips:['1','red']}); - $('#site_edate').css('border-color','red'); + yes: function (index, layers, request) { + var site_edate = $('#site_edate'), site_edate_val = site_edate.val(); + if (site_edate_val != '') { + request({'edate': site_edate_val === 'Forever' ? '0000-00-00' : site_edate_val}); + } else { + layer.tips('Input expired date', '#site_edate', {tips: ['1', 'red']}); + $('#site_edate').css('border-color', 'red'); $('#site_edate').click(); - setTimeout(function(){ + setTimeout(function () { $('#site_edate').removeAttr('style'); - },3000); + }, 3000); return false; } } } - },{ - title:lan.site.set_php_version, - url:'/site?action=set_site_php_version_multiple', - paramName:'sites_id', //列表参数名,可以为空 - paramId:'id', // 需要传入批量的id - theadName:'Name', + }, { + title: lan.site.set_php_version, + url: '/site?action=set_site_php_version_multiple', + paramName: 'sites_id', //列表参数名,可以为空 + paramId: 'id', // 需要传入批量的id + theadName: 'Name', refresh: true, - confirm:{ - title:'Batch set php version', - area:'420px', - content:'
                                  PHP version
                                  • Please select the version according to your program requirements.
                                  • If not necessary, please try not to use PHP 5.2, which will reduce your server security.
                                  • PHP 7 does not support mysql extension, mysqli and mysql_pdo will be installed by default.
                                  ', - success:function(){ - bt.site.get_all_phpversion(function(res){ + confirm: { + title: 'Batch set php version', + area: '420px', + content: '
                                  PHP version
                                  • Please select the version according to your program requirements.
                                  • If not necessary, please try not to use PHP 5.2, which will reduce your server security.
                                  • PHP 7 does not support mysql extension, mysqli and mysql_pdo will be installed by default.
                                  ', + success: function () { + bt.site.get_all_phpversion(function (res) { var html = ''; - $.each(res,function(index,item){ - html += ''; + $.each(res, function (index, item) { + html += ''; }); $('[name="versions"]').html(html); }); }, - yes:function(index,layers,request){ - request({version:$('[name="versions"]').val()}); + yes: function (index, layers, request) { + request({version: $('[name="versions"]').val()}); } } - },{ - title:lan.site.set_category, - url:'/site?action=set_site_type', - paramName:'site_ids', //列表参数名,可以为空 - paramId:'id', // 需要传入批量的id + }, { + title: lan.site.set_category, + url: '/site?action=set_site_type', + paramName: 'site_ids', //列表参数名,可以为空 + paramId: 'id', // 需要传入批量的id refresh: true, - beforeRequest:function(list){ + beforeRequest: function (list) { var arry = []; - $.each(list,function(index,item){ + $.each(list, function (index, item) { arry.push(item.id); }); return JSON.stringify(arry); }, - confirm:{ - title:'Batch set category', - content:'
                                  Site category
                                  ', - success:function(){ - bt.site.get_type(function(res){ + confirm: { + title: 'Batch set category', + content: '
                                  Site category
                                  ', + success: function () { + bt.site.get_type(function (res) { var html = ''; - $.each(res,function(index,item){ - html += ''; + $.each(res, function (index, item) { + html += ''; }); $('[name="site_types"]').html(html); }); }, - yes:function(index,layers,request){ - request({id:$('[name="site_types"]').val()}); + yes: function (index, layers, request) { + request({id: $('[name="site_types"]').val()}); } }, - tips:false, + tips: false, refresh: true, - success:function(res,list,that){ + success: function (res, list, that) { var html = ''; - $.each(list,function(index,item){ - html += ''+ item.name +'
                                  '+ res.msg +'
                                  '; + $.each(list, function (index, item) { + html += '' + item.name + '
                                  ' + res.msg + '
                                  '; }); - that.$batch_success_table({title:'Batch set category',th:'Site name',html:html}); + that.$batch_success_table({title: 'Batch set category', th: 'Site name', html: html}); that.$refresh_table_list(true); } - },{ - title:lan.site.del_website, - url:'/site?action=DeleteSite', + }, { + title: lan.site.del_website, + url: '/site?action=DeleteSite', // paramName:'sites_id', //列表参数名,可以为空 // paramId:'id', //需要传入批量的id // theadName:'Name', @@ -1870,7 +2038,7 @@ var site = { } }, load: true, - callback: function(that){ + callback: function (that) { // bt.show_confirm("Delete site","Confirm delete the FTP、database、root path of the selected site with the same name", function(){ // var param = {}; // $('.bacth_options input[type=checkbox]').each(function(){ @@ -1883,13 +2051,13 @@ var site = { for (var i = 0; i < that.check_list.length; i++) { ids.push(that.check_list[i].id); } - site.del_site(ids,function(param){ + site.del_site(ids, function (param) { that.start_batch(param, function (list) { layer.closeAll() var html = ''; for (var i = 0; i < list.length; i++) { var item = list[i]; - html += '' + item.name + '
                                  ' + item.request.msg + '
                                  '; + html += '' + item.name + '
                                  ' + item.request.msg + '
                                  '; } site_table.$batch_success_table({ title: 'Batch delete', @@ -1902,43 +2070,43 @@ var site = { } } ], - },{ //分页显示 - type:'page', - positon:['right','bottom'], // 默认在右下角 - pageParam:'p', //分页请求字段,默认为 : p - page:1, //当前分页 默认:1 - numberParam:'limit', //分页数量请求字段默认为 : limit - number:20, //分页数量默认 : 20条 - numberList:[10,20,50,100,200], // 分页显示数量列表 - numberStatus:true, // 是否支持分页数量选择,默认禁用 - jump:true, //是否支持跳转分页,默认禁用 + }, { //分页显示 + type: 'page', + positon: ['right', 'bottom'], // 默认在右下角 + pageParam: 'p', //分页请求字段,默认为 : p + page: 1, //当前分页 默认:1 + numberParam: 'limit', //分页数量请求字段默认为 : limit + number: 20, //分页数量默认 : 20条 + numberList: [10, 20, 50, 100, 200], // 分页显示数量列表 + numberStatus: true, // 是否支持分页数量选择,默认禁用 + jump: true, //是否支持跳转分页,默认禁用 }] }); - $('.tootls_group.tootls_top .pull-left').append('
                                  Classification:
                                    '); - bt.site.get_type(function(res) { - site.reader_site_type(res,site_table); + $('.tootls_group.tootls_top .pull-left').append('
                                    Classification:
                                      '); + bt.site.get_type(function (res) { + site.reader_site_type(res, site_table); }); }, - reader_site_type: function(res,config) { + reader_site_type: function (res, config) { var html = '', active = bt.get_cookie('site_type') || -1, select = $('.site_class_type'); - if(select.find('.bt_select_list li').length > 1) return false - res.unshift({ id: -1, name: "Category manager" }); - $.each(res, function(index, item) { + if (select.find('.bt_select_list li').length > 1) return false + res.unshift({id: -1, name: "Category manager"}); + $.each(res, function (index, item) { html += '
                                    • ' + item.name + '
                                    • '; }); html += '
                                    • Category set
                                    • '; - select.find('.bt_select_value').on('click', function(ev) { + select.find('.bt_select_value').on('click', function (ev) { var $this = this; $(this).next().show(); - $(document).one('click', function() { + $(document).one('click', function () { $($this).next().hide(); }); ev.stopPropagation() }); - select.find('.bt_select_list').unbind('click').on('click', 'li', function() { + select.find('.bt_select_list').unbind('click').on('click', 'li', function () { var id = $(this).data('id'); if (id === 'type_sets') { site.set_class_type(); @@ -1961,33 +2129,35 @@ var site = { select.find('.bt_select_value .bt_select_content').text(select.find('.bt_select_list li.active').text()); } }, - get_list: function(page, search, type) { + get_list: function (page, search, type) { if (page == undefined) page = 1; if (type == '-1' || type == undefined) { type = bt.get_cookie('site_type'); } if (!search) search = $("#SearchValue").val(); - bt.site.get_list(page, search, type, function(rdata) { + bt.site.get_list(page, search, type, function (rdata) { $('.dataTables_paginate').html(rdata.page); var data = rdata.data; var _tab = bt.render({ table: '#webBody', columns: [ - { field: 'id', type: 'checkbox', width: 30 }, + {field: 'id', type: 'checkbox', width: 30}, { field: 'name', title: lan.site.site_name, width: 150, - templet: function(item) { + templet: function (item) { return '' + item.name + ''; }, - sort: function() { site.get_list(); } + sort: function () { + site.get_list(); + } }, { field: 'status', title: lan.site.status, width: 98, - templet: function(item) { + templet: function (item) { var _status = ''; @@ -1997,13 +2167,15 @@ var site = { _status += '' + lan.site.stopped + ' '; } return _status; - },sort: function() { site.get_list(); } + }, sort: function () { + site.get_list(); + } }, { field: 'backup', title: lan.site.backup, width: 105, - templet: function(item) { + templet: function (item) { var backup = lan.site.backup_no; if (item.backup_count > 0) backup = lan.site.backup_yes; return '' + backup + ''; @@ -2012,7 +2184,7 @@ var site = { { field: 'path', title: lan.site.root_dir, - templet: function(item) { + templet: function (item) { var _path = bt.format_path(item.path); return '' + _path + ''; } @@ -2021,43 +2193,44 @@ var site = { field: 'edate', title: lan.site.endtime, width: 127, - templet: function(item) { + templet: function (item) { var _endtime = ''; if (item.edate) _endtime = item.edate; if (item.endtime) _endtime = item.endtime; _endtime = (_endtime == "0000-00-00") ? lan.site.web_end_time : _endtime return '' + _endtime + ''; }, - sort: function() { site.get_list(); } + sort: function () { + site.get_list(); + } }, { field: 'ps', title: lan.site.note, - templet: function(item) { + templet: function (item) { return "" + item.ps + ""; } }, { - field: 'php_version',width:70, title: 'PHP', templet: function (item) { + field: 'php_version', width: 70, title: 'PHP', templet: function (item) { - return ''+item.php_version+''; + return '' + item.php_version + ''; } }, { field: 'ssl', title: 'SSL', templet: function (item) { var _ssl = ''; - if (item.ssl == -1) - { + if (item.ssl == -1) { _ssl = 'Not Set'; - }else{ - var ssl_info = "Certificate: "+item.ssl.issuer+"
                                      Due date: " + item.ssl.notAfter+"
                                      Application date: " + item.ssl.notBefore +"
                                      Domain name: " + item.ssl.dns.join("/"); - if(item.ssl.endtime < 0){ - _ssl = 'Expired'; + } else { + var ssl_info = "Certificate: " + item.ssl.issuer + "
                                      Due date: " + item.ssl.notAfter + "
                                      Application date: " + item.ssl.notBefore + "
                                      Domain name: " + item.ssl.dns.join("/"); + if (item.ssl.endtime < 0) { + _ssl = 'Expired'; - }else if(item.ssl.endtime < 20){ - _ssl = 'Exp in '+(item.ssl.endtime+' days')+''; - }else{ - _ssl = 'Exp in '+item.ssl.endtime+' days'; + } else if (item.ssl.endtime < 20) { + _ssl = 'Exp in ' + (item.ssl.endtime + ' days') + ''; + } else { + _ssl = 'Exp in ' + item.ssl.endtime + ' days'; } } return _ssl; @@ -2068,7 +2241,7 @@ var site = { width: 90, title: lan.site.operate, align: 'right', - templet: function(item) { + templet: function (item) { var opt = ''; var _check = ' onclick="site.site_waf(\'' + item.name + '\')"'; @@ -2082,75 +2255,76 @@ var site = { data: data }) var outTime = ''; - $('.ssl_tips').hover(function(){ - var that = this,tips = $(that).attr('data-tips'); - if(!tips) return false; - outTime = setTimeout(function(){ + $('.ssl_tips').hover(function () { + var that = this, tips = $(that).attr('data-tips'); + if (!tips) return false; + outTime = setTimeout(function () { layer.tips(tips, $(that), { tips: [2, '#20a53a'], //还可配置颜色 - time:0 + time: 0 }); - },500); - },function(){ - outTime != ''?clearTimeout(outTime):''; + }, 500); + }, function () { + outTime != '' ? clearTimeout(outTime) : ''; layer.closeAll('tips'); }) - $('.ssl_tips').click(function(){ + $('.ssl_tips').click(function () { site.web_edit(this); - var timeVal = setInterval(function(){ + var timeVal = setInterval(function () { var content = $('#webedit-con').html(); - if(content != ''){ + if (content != '') { $('.site-menu p:contains("SSL")').click(); clearInterval(timeVal); } - },100); + }, 100); }); - $('.phpversion_tips').click(function(){ + $('.phpversion_tips').click(function () { site.web_edit(this); - var timeVal = setInterval(function(){ + var timeVal = setInterval(function () { var content = $('#webedit-con').html(); - if(content != ''){ + if (content != '') { $('.site-menu p:contains("PHP version")').click(); clearInterval(timeVal); } - },100); + }, 100); }); //浏览器窗口大小变化时调整内容宽度 var ticket_with = $('#webBody').width(), - td_width = (ticket_with-667-$('#webBody th:contains("SSL")').width())/2; - $('#webBody .webPath').css('max-width',td_width); - $(window).resize(function() { + td_width = (ticket_with - 667 - $('#webBody th:contains("SSL")').width()) / 2; + $('#webBody .webPath').css('max-width', td_width); + $(window).resize(function () { var ticket_with = $('#webBody').width(), - td_width = (ticket_with-667-$('#webBody th:contains("SSL")').width())/2; - $('#webBody .webPath').css('max-width',td_width); + td_width = (ticket_with - 667 - $('#webBody th:contains("SSL")').width()) / 2; + $('#webBody .webPath').css('max-width', td_width); }); //设置到期时间 - $('a.setTimes').each(function() { - var _this = $(this); - var _tr = _this.parents('tr'); - var id = _this.attr('id'); - laydate.render({ - elem: '#' + id, //指定元素 - lang: 'en', - min: bt.get_date(1), - max: '2099-12-31', - vlue: bt.get_date(365), - type: 'date', - format: 'yyyy-MM-dd', - trigger: 'click', - btns: ['perpetual', 'confirm'], - theme: '#20a53a', - done: function(dates) { - var item = _tr.data('item'); - bt.site.set_endtime(item.id, dates, function() {}) - } - }); - }) - //}) + $('a.setTimes').each(function () { + var _this = $(this); + var _tr = _this.parents('tr'); + var id = _this.attr('id'); + laydate.render({ + elem: '#' + id, //指定元素 + lang: 'en', + min: bt.get_date(1), + max: '2099-12-31', + vlue: bt.get_date(365), + type: 'date', + format: 'yyyy-MM-dd', + trigger: 'click', + btns: ['perpetual', 'confirm'], + theme: '#20a53a', + done: function (dates) { + var item = _tr.data('item'); + bt.site.set_endtime(item.id, dates, function () { + }) + } + }); + }) + //}) }); }, - site_waf: function(siteName) { + site_waf: function (siteName) { try { site_waf_config(siteName); } catch (err) { @@ -2158,7 +2332,7 @@ var site = { } }, - html_encode: function(html) { + html_encode: function (html) { var temp = document.createElement("div"); //2.然后将要转换的字符串设置为这个元素的innerText(ie支持)或者textContent(火狐,google支持) (temp.textContent != undefined) ? (temp.textContent = html) : (temp.innerText = html); @@ -2167,44 +2341,44 @@ var site = { temp = null; return output; }, - get_types: function(callback) { - bt.site.get_type(function(rdata) { + get_types: function (callback) { + bt.site.get_type(function (rdata) { var optionList = ''; var t_val = bt.get_cookie('site_type'); for (var i = 0; i < rdata.length; i++) { - optionList += '' + optionList += '' } if ($('.dataTables_paginate').next().hasClass('site_type')) $('.site_type').remove(); - $('.dataTables_paginate').after('
                                      ' + optionList + '
                                      '); + $('.dataTables_paginate').after('
                                      ' + optionList + '
                                      '); $('.site_type button').click(function () { var val = $(this).attr('value'); bt.set_cookie('site_type', val); - site.get_list(0,'', val); + site.get_list(0, '', val); $(".site_type button").removeClass('btn-success').addClass('btn-default'); $(this).addClass('btn-success'); }) if (callback) callback(rdata); }); }, - no_firewall: function(obj) { + no_firewall: function (obj) { var typename = bt.get_cookie('serverType'); layer.confirm(lan.site.firewalld_nonactivated_tips.replace('{1}', typename).replace('{2}', typename), { title: typename + lan.site.site_classification, icon: 7, closeBtn: 2, - cancel: function() { + cancel: function () { if (obj) $(obj).prop('checked', false) } - }, function() { + }, function () { window.location.href = '/soft'; - }, function() { + }, function () { if (obj) $(obj).prop('checked', false) }) }, - site_detail: function(id, siteName, page) { + site_detail: function (id, siteName, page) { if (page == undefined) page = '1'; var loadT = bt.load(lan.public.the_get); - bt.pub.get_data('table=backup&search=' + id + '&limit=5&type=0&tojs=site.site_detail&p=' + page, function(frdata) { + bt.pub.get_data('table=backup&search=' + id + '&limit=5&type=0&tojs=site.site_detail&p=' + page, function (frdata) { loadT.close(); var ftpdown = ''; var body = ''; @@ -2222,13 +2396,14 @@ var site = { content: "
                                      • Before restoring data, all data in the root dir of the website will be moved to the panel recycle bin.
                                      " }); } - setTimeout(function() { + setTimeout(function () { $('.sitebackup_page').html(frdata.page); var _tab = bt.render({ table: '#SiteBackupList', columns: [ - { field: 'name', title: lan.site.filename , - templet: function(item) { + { + field: 'name', title: lan.site.filename, + templet: function (item) { var _opt = '' + item.name + '' return _opt; } @@ -2236,16 +2411,16 @@ var site = { { field: 'size', title: lan.site.filesize, - templet: function(item) { + templet: function (item) { return bt.format_size(item.size); } }, - { field: 'addtime', title: lan.site.backup_time }, + {field: 'addtime', title: lan.site.backup_time}, { field: 'opt', title: lan.site.operate, align: 'right', - templet: function(item) { + templet: function (item) { var _opt = 'Restore | '; _opt += '' + lan.site.download + ' | '; _opt += '' + lan.site.del + '' @@ -2255,29 +2430,29 @@ var site = { ], data: frdata.data }); - $('#btn_data_backup').unbind('click').click(function() { - bt.site.backup_data(id, function(rdata) { + $('#btn_data_backup').unbind('click').click(function () { + bt.site.backup_data(id, function (rdata) { if (rdata.status) site.site_detail(id, siteName); site.get_list(); }); }); - $('#SiteBackupList .restore').unbind('click').click(function() { + $('#SiteBackupList .restore').unbind('click').click(function () { var data = {}; - data.file_name = $(this).attr('backup-name'); - data.site_id = $(this).attr('site-id'); + data.file_name = $(this).attr('backup-name'); + data.site_id = $(this).attr('site-id'); // console.log(data); layer.confirm('Are you sure to restore backup file?', { icon: 0, closeBtn: 2, - title: 'Restore backup file', - }, function (index) { - $.post('/files?action=restore_website', data, function(rdata) { + title: 'Restore backup file', + }, function (index) { + $.post('/files?action=restore_website', data, function (rdata) { layer.close(index); site.backup_output_stop = true; layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); }); site.backup_output_logs(); - }); + }); }) }, 100) }); @@ -2287,50 +2462,50 @@ var site = { * @param {object} config 配置参数 * @param {function} callback 回调函数 */ - backup_site_view: function(config, thatC,callback) { + backup_site_view: function (config, thatC, callback) { bt_tools.open({ - title: lan.site.backup_title+' - [ ' + config.name + ' ]', + title: lan.site.backup_title + ' - [ ' + config.name + ' ]', area: '720px', btn: false, skin: 'bt_backup_table', content: '
                                      ', - success: function() { + success: function ($layer) { var backup_table = bt_tools.table({ el: '#bt_backup_table', url: '/data?action=getData', - param: { table: 'backup', search: config.id, type: '0' }, + param: {table: 'backup', search: config.id, type: '0'}, default: "[" + config.name + "] Currently no backup", //数据为空时的默认提示 column: [ - { type: 'checkbox', class: '', width: 20 }, - { fid: 'name', title: lan.site.filename, width: 250, fixed: true }, + {type: 'checkbox', class: '', width: 20}, + {fid: 'name', title: lan.site.filename, width: 250, fixed: true}, { fid: 'size', title: lan.site.filesize, width: 80, type: 'text', - template: function(row, index) { + template: function (row, index) { return bt.format_size(row.size); } }, - { fid: 'addtime', width: 150, title: lan.site.backup_time }, + {fid: 'addtime', width: 150, title: lan.site.backup_time}, { title: lan.site.operate, type: 'group', width: 165, align: 'right', group: [{ - title:'Restore', - event: function(row) { + title: 'Restore', + event: function (row) { var data = {}; - data.file_name = row.name; - data.site_id = config.id; + data.file_name = row.name; + data.site_id = config.id; // console.log(data); layer.confirm('Are you sure to restore backup file?', { icon: 0, closeBtn: 2, title: 'Restore backup file', }, function (index) { - $.post('/files?action=restore_website', data, function(rdata) { + $.post('/files?action=restore_website', data, function (rdata) { layer.close(index); site.backup_output_stop = true; layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); @@ -2338,18 +2513,18 @@ var site = { site.backup_output_logs(); }); } - },{ + }, { title: lan.site.download, - template: function(row, index, ev, key, that) { - return ''+lan.site.download+''; + template: function (row, index, ev, key, that) { + return '' + lan.site.download + ''; } }, { title: lan.site.del, - event: function(row, index, ev, key, that) { - that.del_site_backup({ name: row.name, id: row.id }, function(rdata) { + event: function (row, index, ev, key, that) { + that.del_site_backup({name: row.name, id: row.id}, function (rdata) { bt_tools.msg(rdata); if (rdata.status) { - thatC.$modify_row_data({ backup_count: thatC.event_rows_model.rows.backup_count - 1 }); + thatC.$modify_row_data({backup_count: thatC.event_rows_model.rows.backup_count - 1}); that.$refresh_table_list(); } }); @@ -2363,15 +2538,18 @@ var site = { * @param {object} config * @param {function} callback */ - del_site_backup: function(config, callback) { - bt.confirm({ title: lan.site.del_bak_file, msg: 'The website backup is about to be deleted [' + config.name + '], do you want to continue?' }, function() { - bt_tools.send('site/DelBackup', { id: config.id }, function(rdata) { + del_site_backup: function (config, callback) { + bt.confirm({ + title: lan.site.del_bak_file, + msg: 'The website backup is about to be deleted [' + config.name + '], do you want to continue?' + }, function () { + bt_tools.send('site/DelBackup', {id: config.id}, function (rdata) { if (callback) callback(rdata); }, true); }); } }, - success: function() { + success: function () { if (callback) callback(); $('.bt_backup_table').css('top', (($(window).height() - $('.bt_backup_table').height()) / 2) + 'px'); }, @@ -2381,11 +2559,11 @@ var site = { list: [{ title: 'Backup', active: true, - event: function(ev, that) { - bt.site.backup_data(config.id, function(rdata) { + event: function (ev, that) { + bt.site.backup_data(config.id, function (rdata) { bt_tools.msg(rdata); if (rdata.status) { - thatC.$modify_row_data({ backup_count: thatC.event_rows_model.rows.backup_count + 1 }); + thatC.$modify_row_data({backup_count: thatC.event_rows_model.rows.backup_count + 1}); that.$refresh_table_list(); } }); @@ -2399,18 +2577,26 @@ var site = { url: '/site?action=DelBackup', paramId: 'id', load: true, - callback: function(that) { - bt.confirm({ title: 'Delete site backups in bulk', msg: 'Do you want to delete selected site backups in batches?', icon: 0 }, function(index) { + callback: function (that) { + bt.confirm({ + title: 'Delete site backups in bulk', + msg: 'Do you want to delete selected site backups in batches?', + icon: 0 + }, function (index) { layer.close(index); - that.start_batch({}, function(list) { + that.start_batch({}, function (list) { var html = ''; for (var i = 0; i < list.length; i++) { var item = list[i]; - html += '' + item.name + '' + item.request.msg + ''; + html += '' + item.name + '' + item.request.msg + ''; } - backup_table.$batch_success_table({ title: 'Delete site backups in bulk', th: 'file name', html: html }); + backup_table.$batch_success_table({ + title: 'Delete site backups in bulk', + th: 'file name', + html: html + }); backup_table.$refresh_table_list(true); - thatC.$modify_row_data({ backup_count: thatC.event_rows_model.rows.backup_count - list.length }); + thatC.$modify_row_data({backup_count: thatC.event_rows_model.rows.backup_count - list.length}); }); }); } @@ -2421,9 +2607,9 @@ var site = { pageParam: 'p', //分页请求字段,默认为 : p page: 1, //当前分页 默认:1 numberParam: 'limit', -   //分页数量请求字段默认为 : limit + //分页数量请求字段默认为 : limit number: 10, -   //分页数量默认 : 20条 + //分页数量默认 : 20条 }] }); } @@ -2439,9 +2625,9 @@ var site = { closeBtn: 0, content: '
                                      ', }); - var show_output = setInterval(function(){ - $.post('/files?action=get_progress', function(rdata){ - if(site.backup_output_stop) { + var show_output = setInterval(function () { + $.post('/files?action=get_progress', function (rdata) { + if (site.backup_output_stop) { layer.close(layerT); clearInterval(show_output); } @@ -2454,283 +2640,337 @@ var site = { * @description 添加站点 */ add_site: function (callback) { + var typeId = bt.get_cookie('site_type'); var add_web = bt_tools.form({ - data:{}, //用于存储初始值和编辑时的赋值内容 - class:'', - form:[{ - label: lan.site.add_site.domain, - group:[{ - type:'textarea', //当前表单的类型 支持所有常规表单元素、和复合型的组合表单元素 - name:'webname', //当前表单的name - style:{'width':'440px','height':'100px','line-height':'22px'}, - tips:{ //使用hover的方式显示提示 - text: lan.site.domain_help, - style:{top:'15px',left:'15px'}, - }, - keyup:function(value,form,that,config,ev){ //键盘事件 - var array = value.webname.split("\n"),ress = array[0].split(":")[0], - oneVal = bt.strim(ress.replace(new RegExp(/([-.])/g), '_')),defaultPath = $('#defaultPath').text(),is_oneVal = ress.length > 0; - that.$set_find_value(is_oneVal?{ - 'ftp_username':'ftp_'+ oneVal,'ftp_password':bt.get_random(16), - 'datauser':is_oneVal?('sql_'+ oneVal.substr(0, 16)):'','datapassword':bt.get_random(16), - 'ps':oneVal, - 'path':bt.rtrim(defaultPath,'/') + '/'+ ress - }:{'ftp_username':'','ftp_password':'','datauser':'','datapassword':'','ps':'','path':bt.rtrim(defaultPath,'/')}); - if(bt.check_domain(ress)){ - form['redirect'].parents('.block').removeClass('hide'); - if(ress.indexOf('www.') !== 0){ - form['redirect'].next().find('span').text('www.'+ress) - }else if(ress.indexOf('www.') === 0){ - form['redirect'].next().find('span').text(ress.replace(/^www\./,'')) - } - }else{ - form['tourl'].parents('.line').addClass('hide'); - form['redirect'].parents('.block').addClass('hide'); + data: {}, //用于存储初始值和编辑时的赋值内容 + class: '', + form: [{ + label: lan.site.add_site.domain, + group: [{ + type: 'textarea', //当前表单的类型 支持所有常规表单元素、和复合型的组合表单元素 + name: 'webname', //当前表单的name + style: {'width': '440px', 'height': '100px', 'line-height': '22px'}, + tips: { //使用hover的方式显示提示 + text: lan.site.domain_help, + style: {top: '15px', left: '15px'}, + }, + keyup: function (value, form, that, config, ev) { //键盘事件 + var array = value.webname.split("\n"), ress = array[0].split(":")[0], + oneVal = bt.strim(ress.replace(new RegExp(/([-.])/g), '_')), + defaultPath = $('#defaultPath').text(), is_oneVal = ress.length > 0; + that.$set_find_value(is_oneVal ? { + 'ftp_username': 'ftp_' + oneVal, + 'ftp_password': bt.get_random(16), + 'datauser': is_oneVal ? ('sql_' + oneVal.substr(0, 16)) : '', + 'datapassword': bt.get_random(16), + 'ps': oneVal, + 'path': bt.rtrim(defaultPath, '/') + '/' + ress + } : { + 'ftp_username': '', + 'ftp_password': '', + 'datauser': '', + 'datapassword': '', + 'ps': '', + 'path': bt.rtrim(defaultPath, '/') + }); + if (bt.check_domain(ress)) { + form['redirect'].parents('.block').removeClass('hide'); + if (ress.indexOf('www.') !== 0) { + form['redirect'].next().find('span').text('www.' + ress) + } else if (ress.indexOf('www.') === 0) { + form['redirect'].next().find('span').text(ress.replace(/^www\./, '')) } + } else { + form['tourl'].parents('.line').addClass('hide'); + form['redirect'].parents('.block').addClass('hide'); } - },{ - type:'checkbox', - block:true, - block_class: 'redirect_check', - hide:true, - name:'redirect', - label_tips:'Add [] domain name to the main domain name', - event:function(value,form,that,config,ev){ - var domain = form['redirect'].next().find('span').text(), - domain_textarea = form['webname'], - domainList = domain_textarea.val().split('\n'), - domain_one = domainList[0].split(":")[0]; - if(value['redirect'] == 'on'){ - domain_textarea.val(domain_textarea.val() + '\r' + domain); - form['tourl'].parents('.line').removeClass('hide'); - var radio_list = form['tourl'].parents('.line').find('.redirect_tourl'); - $(".redirect_tourl:eq(1)").find('label').html('Redirect the main domain name [ '+ domain_one +'] to ['+ domain +'] domain name'); - $(".redirect_tourl:eq(2)").find('label').html('Redirect the ['+ domain +'] domain name to the main domain ['+ domain_one +']'); - }else{ - for(var i = domainList.length-1;i >= 0;i--){ - if(domainList[i] === domain) domainList.splice(i,1); - } - domain_textarea.val(domainList.join('\n')); - form['tourl'].parents('.line').addClass('hide'); + } + }, { + type: 'checkbox', + block: true, + block_class: 'redirect_check', + hide: true, + name: 'redirect', + label_tips: 'Add [] domain name to the main domain name', + event: function (value, form, that, config, ev) { + var domain = form['redirect'].next().find('span').text(), + domain_textarea = form['webname'], + domainList = domain_textarea.val().split('\n'), + domain_one = domainList[0].split(":")[0]; + if (value['redirect'] == 'on') { + domain_textarea.val(domain_textarea.val() + '\r' + domain); + form['tourl'].parents('.line').removeClass('hide'); + var radio_list = form['tourl'].parents('.line').find('.redirect_tourl'); + $(".redirect_tourl:eq(1)").find('label').html('Redirect the main domain name [ ' + domain_one + '] to [' + domain + '] domain name'); + $(".redirect_tourl:eq(2)").find('label').html('Redirect the [' + domain + '] domain name to the main domain [' + domain_one + ']'); + } else { + for (var i = domainList.length - 1; i >= 0; i--) { + if (domainList[i] === domain) domainList.splice(i, 1); } + domain_textarea.val(domainList.join('\n')); + form['tourl'].parents('.line').addClass('hide'); } - }] - },{ - label:'Redirect', - hide:true, - group:[{ - type:'radio', - name:'tourl', - block:true, - block_class: 'redirect_tourl', - label_tips:['No', + } + }] + }, { + label: 'Redirect', + hide: true, + group: [{ + type: 'radio', + name: 'tourl', + block: true, + block_class: 'redirect_tourl', + label_tips: ['No', 'Redirect the main domain name [] to [] domain name', 'Redirect the [] domain name to the main domain []'], - }] - },{ - label:lan.site.add_site.description, - group:{ - type:'text', - name:'ps', - width:'400px', - placeholder:lan.note_ph, //默认标准备注提示 - } - },{ - label:lan.site.add_site.root, - group:{ - type:'text', - width:'400px', - name:'path', - icon:{ - type:'glyphicon-folder-open', - event:function(ev){ - } - }, - value:'/www/wwwroot', - placeholder:lan.site.add_site.root_ph, - } - },{ - label:lan.site.add_site.ftp, - group:[{ - type:'select', - name:'ftp', - width:'135px', - disabled:(function(){ - if(bt.config['pure-ftpd']) return !bt.config['pure-ftpd'].setup; - return true; - }()), - list:[ - {title:lan.site.add_site.dont_create,value:false}, - {title:lan.site.add_site.create,value:true} - ], - change:function(value,form,that,config,ev){ - if(value['ftp'] === 'true'){ - form['ftp_username'].parents('.line').removeClass('hide'); - }else{ - form['ftp_username'].parents('.line').addClass('hide'); - } - } - },(function(){ - if(bt.config['pure-ftpd']['setup']) return {}; - return { - type:'link', - title:'FTP is not installed, click Install', - event:function(ev){ - bt.soft.install('pureftpd'); - } - } - }())] - },{ - label:lan.site.add_site.ftp_set, - hide:true, - group:[ - {type:'text',name:'ftp_username',placeholder:lan.site.add_site.ftp_ph,width:'175px',style:{'margin-right':'15px'}}, - {label:lan.site.add_site.password,type:'text',placeholder:lan.site.add_site.ftp_password,name:'ftp_password',width:'175px'} - ], - help:{ - list:[lan.site.ftp_help], - } - },{ - label:lan.site.add_site.database, - group:[{ - type:'select', - name:'sql', - width:'135px', - disabled:(function(){ - if(bt.config['mysql']) return !bt.config['mysql'].setup; - return true; - }()), - list:[ - {title:lan.site.add_site.dont_create,value:false}, - {title:'MySQL',value:'MySQL'}, - {title:'SQLServer',value:'SQLServer',disabled:true,tips:lan.public_backup.unsupport_sqlserver} - ], - change:function(value,form,that,config,ev){ - if(value['sql'] === 'MySQL'){ - form['datauser'].parents('.line').removeClass('hide'); - form['codeing'].parents('.bt_select_updown').removeClass('hide'); - }else{ - form['datauser'].parents('.line').addClass('hide'); - form['codeing'].parents('.bt_select_updown').addClass('hide'); - } - } - },(function(){ - if(bt.config.mysql.setup) return {}; - return { - type:'link', - title:'Database not installed, click Install', - event:function(ev){ - bt.soft.install('pureftpd'); - } - } - }()),{ - type:'select', - name:'codeing', - hide:true, - width:'135px', - list:[ - {title:'utf8',value:'utf8'}, - {title:'utf8mb4',value:'utf8mb4'}, - {title:'gbk',value:'gbk'}, - {title:'big5',value:'big5'} - ] - }] - },{ - label:lan.site.add_site.database_set, - hide:true, - group:[ - {type:'text',name:'datauser',placeholder:lan.site.add_site.database_ph,width:'175px',style:{'margin-right':'15px'}}, - {label:lan.site.add_site.password,type:'text',placeholder:lan.site.add_site.database_password,name:'datapassword',width:'175px'} - ], - help:{ - class:'', - style:'', - list:[lan.site.database_help], - } - },{ - label:lan.site.add_site.php_version, - group:[ - { - type:'select', - name:'version', - width:'135px', - list:{ - url:'/site?action=GetPHPVersion', - dataFilter:function(res){ - var arry = []; - for(var i = res.length-1; i>=0;i--){ - var item = res[i]; - arry.push({title:item.name,value:item.version}); - } - return arry; - } - } - } - ] - },{ - label:lan.site.add_site.category, - group:[ - { - type:'select', - name:'type_id', - width:'135px', - list:{ - url:'/site?action=get_site_types', - dataFilter:function(res){ - var arry = []; - $.each(res,function(index,item){ - arry.push({title:item.name,value:item.id}); - }); - return arry; - } - } - } - ] - },{ - label:'SSL', - class:'ssl_checkbox', - help:{ - style:'color: red;line-height: 17px;', - list:['If you need to apply for SSL, please make sure that the domain name has added A record resolution for the domain name'], - }, - group:[{ - type:'checkbox', - name:'set_ssl', - title:'Apply for SSL', - class:'site_ssl_check', - style:{'margin-right': '10px'} - },{ - type:'checkbox', - name:'force_ssl', - class:'site_ssl_check', - title:'HTTP redirect to HTTPS', - event:function(value,form,that,config,ev){ - var force_ssl = $(this).is(':checked'); - if(force_ssl){ - $('.site_ssl_check:eq(0)').find('i').addClass('active'); - $('input[name=set_ssl]').prop('checked',force_ssl); - } - } - }] + }] + }, { + label: lan.site.add_site.description, + group: { + type: 'text', + name: 'ps', + width: '400px', + placeholder: lan.note_ph, //默认标准备注提示 } + }, { + label: lan.site.add_site.root, + group: { + type: 'text', + width: '400px', + name: 'path', + icon: { + type: 'glyphicon-folder-open', + event: function (ev) { + } + }, + value: '/www/wwwroot', + placeholder: lan.site.add_site.root_ph, + } + }, { + label: lan.site.add_site.ftp, + group: [{ + type: 'select', + name: 'ftp', + width: '135px', + disabled: (function () { + if (bt.config['pure-ftpd']) return !bt.config['pure-ftpd'].setup; + return true; + }()), + list: [ + {title: lan.site.add_site.dont_create, value: false}, + {title: lan.site.add_site.create, value: true} + ], + change: function (value, form, that, config, ev) { + if (value['ftp'] === 'true') { + form['ftp_username'].parents('.line').removeClass('hide'); + } else { + form['ftp_username'].parents('.line').addClass('hide'); + } + } + }, (function () { + if (bt.config['pure-ftpd']['setup']) return {}; + return { + type: 'link', + title: 'FTP is not installed, click Install', + name: 'installed_ftp', + event: function (ev) { + bt.soft.install('pureftpd'); + } + } + }())] + }, { + label: lan.site.add_site.ftp_set, + hide: true, + group: [ + { + type: 'text', + name: 'ftp_username', + placeholder: lan.site.add_site.ftp_ph, + width: '175px', + style: {'margin-right': '15px'} + }, + { + label: lan.site.add_site.password, + type: 'text', + placeholder: lan.site.add_site.ftp_password, + name: 'ftp_password', + width: '175px' + } + ], + help: { + list: [lan.site.ftp_help], + } + }, { + label: lan.site.add_site.database, + group: [{ + type: 'select', + name: 'sql', + width: '135px', + disabled: (function () { + if (bt.config['mysql']) return !bt.config['mysql'].setup; + return true; + }()), + list: [ + {title: lan.site.add_site.dont_create, value: false}, + {title: 'MySQL', value: 'MySQL'}, + { + title: 'SQLServer', + value: 'SQLServer', + disabled: true, + tips: lan.public_backup.unsupport_sqlserver + } + ], + change: function (value, form, that, config, ev) { + if (value['sql'] === 'MySQL') { + form['datauser'].parents('.line').removeClass('hide'); + form['codeing'].parents('.bt_select_updown').removeClass('hide'); + } else { + form['datauser'].parents('.line').addClass('hide'); + form['codeing'].parents('.bt_select_updown').addClass('hide'); + } + } + }, (function () { + if (bt.config.mysql.setup) return {}; + return { + type: 'link', + title: 'Database not installed, click Install', + name: 'installed_database', + event: function (ev) { + bt.soft.install('mysql'); + } + } + }()), { + type: 'select', + name: 'codeing', + hide: true, + width: '135px', + list: [ + {title: 'utf8', value: 'utf8'}, + {title: 'utf8mb4', value: 'utf8mb4'}, + {title: 'gbk', value: 'gbk'}, + {title: 'big5', value: 'big5'} + ] + }] + }, { + label: lan.site.add_site.database_set, + hide: true, + group: [ + { + type: 'text', + name: 'datauser', + placeholder: lan.site.add_site.database_ph, + width: '175px', + style: {'margin-right': '15px'} + }, + { + label: lan.site.add_site.password, + type: 'text', + placeholder: lan.site.add_site.database_password, + name: 'datapassword', + width: '175px' + } + ], + help: { + class: '', + style: '', + list: [lan.site.database_help], + } + }, { + label: lan.site.add_site.php_version, + group: [ + { + type: 'select', + name: 'version', + width: '135px', + list: { + url: '/site?action=GetPHPVersion', + dataFilter: function (res) { + var arry = []; + for (var i = res.length - 1; i >= 0; i--) { + var item = res[i]; + arry.push({title: item.name, value: item.version}); + } + return arry; + } + } + } + ] + }, { + label: lan.site.add_site.category, + group: [ + { + type: 'select', + name: 'type_id', + width: '135px', + list: { + url: '/site?action=get_site_types', + dataFilter: function (res) { + var arry = []; + $.each(res, function (index, item) { + arry.push({title: item.name, value: item.id}); + }); + return arry; + }, + success: function (res, formObj) { + setTimeout(function () { + var index = -1; + for (var i = 0; i < res.length; i++) { + if (res[i].id == typeId) { + index = i; + break; + } + } + if (index != -1) formObj.element.find('.bt_select_updown[data-name="type_id"]').find('.bt_select_list li').eq(index).click(); + }, 100); + } + } + } + ] + }, { + label: 'SSL', + class: 'ssl_checkbox', + help: { + style: 'color: red;line-height: 17px;', + list: ['If you need to apply for SSL, please make sure that the domain name has added A record resolution for the domain name'], + }, + group: [{ + type: 'checkbox', + name: 'set_ssl', + title: 'Apply for SSL', + class: 'site_ssl_check', + style: {'margin-right': '10px'} + }, { + type: 'checkbox', + name: 'force_ssl', + class: 'site_ssl_check', + title: 'HTTP redirect to HTTPS', + event: function (value, form, that, config, ev) { + var force_ssl = $(this).is(':checked'); + if (force_ssl) { + $('.site_ssl_check:eq(0)').find('i').addClass('active'); + $('input[name=set_ssl]').prop('checked', force_ssl); + } + } + }] + } ] }); var bath_web = bt_tools.form({ - class:'plr10', - form:[{ - line_style:{'position':'relative'}, - group:{ - type:'textarea', //当前表单的类型 支持所有常规表单元素、和复合型的组合表单元素 - name:'bath_code', //当前表单的name - style:{'width':'560px','height':'180px','line-height':'22px','font-size':'13px'}, - value:lan.site.add_site.bath_code_ph, + class: 'plr10', + form: [{ + line_style: {'position': 'relative'}, + group: { + type: 'textarea', //当前表单的类型 支持所有常规表单元素、和复合型的组合表单元素 + name: 'bath_code', //当前表单的name + style: {'width': '560px', 'height': '180px', 'line-height': '22px', 'font-size': '13px'}, + value: lan.site.add_site.bath_code_ph, } - },{ - group:{ - type:'help', - style:{'margin-top':'0'}, - class:'none-list-style', - list:[ + }, { + group: { + type: 'help', + style: {'margin-top': '0'}, + class: 'none-list-style', + list: [ lan.site.add_site.bath_tips1, lan.site.add_site.bath_tips2, lan.site.add_site.bath_tips3, @@ -2743,98 +2983,219 @@ var site = { } }] }); - var web_tab = bt_tools.tab({ - class:'pd20', - type:0, - theme:{nav:'mlr20'}, - active:1, //激活TAB下标 - list:[{ - title:lan.site.add_site.create_site, - name:'createSite', - content:add_web.$reader_content(), - success:function(){ - add_web.$event_bind(); + var deploy_wp = bt_tools.form({ + form:[{ + label:'Domain', + group:{ + type:'text', + name:'domain', + width:'400px', + placeholder:'Your website domain name' } },{ - title:lan.site.add_site.batch_creat, - name:'batchCreation', - content:bath_web.$reader_content(), - success:function(){ + label:'Website Title', + group:{ + type:'text', + name:'weblog_title', + width:'400px', + placeholder:'Website title for wordpress' + } + },{ + label:'Language', + group:[{ + type:'select', + name:'language', + width: '230px', + list:{ + url:'/site?action=get_language', + dataFilter:function(rlist){ + var lan = []; + $.each(rlist.msg,function(index,item){ + lan.push({title:item,value:index}) + }) + return lan + } + } + }] + },{ + label:'PHP version', + group:[{ + type:'select', + name:'php_version', + width:'230px', + list:add_web['config']['form'][8]['group'][0]['list'] + }] + },{ + label:'User name', + group:{ + type:'text', + name:'user_name', + width:'400px', + placeholder:'WordPress backend user' + } + },{ + label: 'Password', + group: [ + { + type: 'text', + name: 'admin_password', + placeholder: 'WordPress backend password', + width: '230px', + style: {'margin-right': '15px'} + }, + { + type: 'checkbox', + name: 'pw_weak', + title: 'Allow weak passwords', + } + ] + },{ + label:'Email', + group:{ + type:'text', + name:'admin_email', + width:'400px', + placeholder:'Your email address' + } + },{ + label:'Prefix', + group:{ + type:'text', + name:'prefix', + width:'400px', + value:'wp_', + placeholder:'Wordpress table name prefix' + } + },{ + label: 'Enable cache', + style:{'min-height': '0','line-height': '15px','margin-bottom':' 0'}, + group: [ + { + type: 'checkbox', + name: 'enable_cache', + title: 'Enable caching, currently only supports nginx', + } + ] + }] + }) + + + var web_tab = bt_tools.tab({ + class: 'pd20', + type: 0, + theme: {nav: 'mlr20'}, + active: 1, //激活TAB下标 + list: [{ + title: lan.site.add_site.create_site, + name: 'createSite', + content: add_web.$reader_content(), + success: function () { + add_web.$event_bind(); + } + }, { + title: lan.site.add_site.batch_creat, + name: 'batchCreation', + content: bath_web.$reader_content(), + success: function () { bath_web.$event_bind(); } - }], - success:function(){ - - } + },{ + title:'Wordpress deploy', + name:'wordpressDeploy', + content:'', + success:function (el) { + el.html(deploy_wp.$reader_content()) + deploy_wp.$event_bind(); + $(el).find('form .line:last-child .tname').css({'height':'22px','line-height':'22px'}) + } + }] }); bt_tools.open({ - title:lan.site.add_site.add_site_title, - skin:'custom_layer', - btn:[lan.public.submit,lan.site.no], - content:web_tab.$reader_content(), - success:function(){ + title: lan.site.add_site.add_site_title, + skin: 'custom_layer', + btn: [lan.public.submit, lan.site.no], + content: web_tab.$reader_content(), + success: function ($layer) { web_tab.$init(); + + $layer.find('.tab-con').scroll(function () { + $layer.find('.bt_select_list').removeClass('show') + }) + // $layer.find('.layui-layer-content, .tab-con').css('overflow', $(window).height() > $layer.height() ? 'visible' : 'auto'); }, - yes:function(indexs){ - var formValue = !web_tab.active?add_web.$get_form_value():bath_web.$get_form_value(); - if(formValue.webname === ''){ - bt.msg({status:false,msg:'网站域名不能为空!'}) + yes: function (indexs) { + var tabContent = add_web, + tabActive = web_tab.active; + switch(tabActive){ + case 1: + tabContent = bath_web + break; + case 2: + tabContent = deploy_wp + break; + } + var formValue = tabContent.$get_form_value(); + if (tabActive != 2 && formValue.webname === '') { + bt.msg({status: false, msg: '网站域名不能为空!'}) return false; } - if(!web_tab.active){ // 创建站点 + + if (tabActive == 0) { // 创建站点 var loading = bt.load(); add_web.$get_form_element(true); - if(formValue.webname === ''){ + if (formValue.webname === '') { add_web.form_element.webname.focus(); - bt_tools.msg(lan.public.domain_format_not_right,2); - return ; + bt_tools.msg(lan.public.domain_format_not_right, 2); + return; } - var webname = bt.replace_all(formValue.webname,'http[s]?:\\/\\/',''),web_list = webname.split('\n'), - param = {webname:{domain:'',domainlist:[],count:0},type:'PHP',port:80},arry = ['ps',['path',lan.site.site_menu_2],'type_id','version','ftp','sql','ftp_username','ftp_password','datauser','datapassword','codeing'] - for(var i=0;i----'+ item +''; + if (callback) callback(rdata); + $.each(rdata.error, function (key, item) { + _html += '' + key + '----' + item + ''; }); - $.each(rdata.success,function(key,item){ - _html += ''+ key +''+ (item.ftp_status?''+lan.site.add_site.success +'':''+lan.site.add_site.not_created +'') +''+ (item.db_status?''+lan.site.add_site.success +'':''+lan.site.add_site.not_created +'') +''+lan.site.add_site.created +''; + $.each(rdata.success, function (key, item) { + _html += '' + key + '' + (item.ftp_status ? '' + lan.site.add_site.success + '' : '' + lan.site.add_site.not_created + '') + '' + (item.db_status ? '' + lan.site.add_site.success + '' : '' + lan.site.add_site.not_created + '') + '' + lan.site.add_site.created + ''; }); bt.open({ - type:1, - title:lan.site.add_site.batch_add_site, - area:['500px','450px'], - shadeClose:false, - closeBtn:2, - content:'
                                      '+ _html +'
                                      '+lan.site.add_site.site_name +'FTP'+lan.site.add_site.database +''+lan.site.add_site.opt_result +'
                                      ', - success:function(){ - $('.fiexd_thead').scroll(function(){ + type: 1, + title: lan.site.add_site.batch_add_site, + area: ['500px', '450px'], + shadeClose: false, + closeBtn: 2, + content: '
                                      ' + _html + '
                                      ' + lan.site.add_site.site_name + 'FTP' + lan.site.add_site.database + '' + lan.site.add_site.opt_result + '
                                      ', + success: function () { + $('.fiexd_thead').scroll(function () { var scrollTop = this.scrollTop; this.querySelector('thead').style.transform = 'translateY(' + scrollTop + 'px)'; }); } }); - }else{ + } else { bt.msg(rdata); } }); + } else {//wp一键部署 + var param = {webname: {domain: '', domainlist: [], count: 0},type:'PHP',port:80,type_id:0,ftp:false,sql:'MySQL',codeing:'utf8',set_ssl:0,force_ssl:0,project_type:'WP'} + if(formValue.domain == '') return layer.msg('Wordpress domain name cannot be empty',{icon:2}) + if(formValue.weblog_title == '') return layer.msg('Wordpress site title cannot be empty',{icon:2}) + if(formValue.user_name == '') return layer.msg('Wordpress backend user cannot be empty',{icon:2}) + if(formValue.admin_password == '') return layer.msg('Wordpress backend password cannot be empty',{icon:2}) + if(formValue.admin_email == '') return layer.msg('Email address cannot be empty',{icon:2}) + if(formValue.prefix == '') return layer.msg('Wordpress table name prefix cannot be empty',{icon:2}) + + var _domain = bt.strim(formValue.domain.replace(new RegExp(/([-.])/g), '_')); + param['webname']['domain'] = formValue.domain + param['webname'] = JSON.stringify(param['webname']); + + param['path'] = '/www/wwwroot/'+formValue.domain + param['ps'] = _domain + param['version'] = formValue.php_version + param['datauser'] = 'sql_'+_domain + param['datapassword'] = bt.get_random(16) + + // 密码强度判断 + param['password'] = formValue['admin_password'] + param['pw_weak'] = formValue['pw_weak']?'on':'off' + param['email'] = formValue['admin_email'] + + var loading = bt.load('Creating website, please wait...'); + // 1.通过主域名生成域名文件地址等信息 + bt.send('AddSite', 'site/AddSite', param, function (rdata) { + loading.close(); + if(typeof rdata.status === 'boolean' && !rdata.status) return layer.msg(rdata.msg,{icon: 2, time: 0, shade: 0.3, shadeClose: true}) + // 2.通过回调的siteId,d_id部署wp + if(rdata.databaseStatus){ + formValue['d_id'] = rdata.d_id; + formValue['s_id'] = rdata.siteId; + formValue['enable_cache'] = formValue['enable_cache']?1:0 + formValue['pw_weak'] = formValue['pw_weak']?'on':'off' + + var loadingWp = bt.load('Deploying wordpress, please wait...'); + bt.send('deploy_wp','site/deploy_wp',formValue,function(deploy){ + loadingWp.close(); + if(deploy.status){ + layer.close(indexs); + if(callback) callback(deploy) + } + layer.msg(deploy.msg,{icon:deploy.status?1:2}) + }) + } + }) } } }); }, - set_default_page: function() { + set_default_page: function () { bt.open({ type: 1, area: '460px', @@ -2956,27 +3367,37 @@ var site = { shift: 0, content: '
                                      ' }); - setTimeout(function() { - $('.change-default button').click(function() { - bt.site.get_default_path($(this).index(), function(path) { + setTimeout(function () { + $('.change-default button').click(function () { + bt.site.get_default_path($(this).index(), function (path) { bt.pub.on_edit_file(0, path); }) }) }, 100) }, - set_default_site: function() { - bt.site.get_default_site(function(rdata) { + set_default_site: function () { + bt.site.get_default_site(function (rdata) { var arrs = []; - arrs.push({ title: lan.site.default_site_not_set, value: '0' }) - for (var i = 0; i < rdata.sites.length; i++) arrs.push({ title: rdata.sites[i].name, value: rdata.sites[i].name }) + arrs.push({title: lan.site.default_site_not_set, value: '0'}) + for (var i = 0; i < rdata.sites.length; i++) arrs.push({ + title: rdata.sites[i].name, + value: rdata.sites[i].name + }) var form = { title: lan.site.default_site_yes, area: '530px', - list: [{ title: lan.site.default_site, name: 'defaultSite', width: '300px', value: rdata.defaultSite, type: 'select', items: arrs }], + list: [{ + title: lan.site.default_site, + name: 'defaultSite', + width: '300px', + value: rdata.defaultSite, + type: 'select', + items: arrs + }], btns: [ bt.form.btn.close(), - bt.form.btn.submit(lan.site.submit, function(rdata, load) { - bt.site.set_default_site(rdata.defaultSite, function(rdata) { + bt.form.btn.submit(lan.site.submit, function (rdata, load) { + bt.site.set_default_site(rdata.defaultSite, function (rdata) { load.close(); bt.msg(rdata); }) @@ -2988,10 +3409,10 @@ var site = { }) }, //PHP-CLI - get_cli_version: function() { - $.post('/config?action=get_cli_php_version', {}, function(rdata) { + get_cli_version: function () { + $.post('/config?action=get_cli_php_version', {}, function (rdata) { if (rdata.status === false) { - layer.msg(rdata.msg, { icon: 2 }); + layer.msg(rdata.msg, {icon: 2}); return; } var _options = ''; @@ -3001,17 +3422,17 @@ var site = { _options += ''; } var body = '
                                      \ -
                                      \ - ' + lan.site.php_cli_ver + '\ -
                                      \ - \ -
                                      \ -
                                      \ -
                                        \ -
                                      • ' + lan.site.php_cli_tips1 + '
                                      • \ -
                                      • ' + lan.site.php_cli_tips2 + '
                                      • \ -
                                      \ -
                                      '; +
                                      \ + ' + lan.site.php_cli_ver + '\ +
                                      \ + \ +
                                      \ +
                                      \ +
                                        \ +
                                      • ' + lan.site.php_cli_tips1 + '
                                      • \ +
                                      • ' + lan.site.php_cli_tips2 + '
                                      • \ +
                                      \ +
                                      '; layer.open({ type: 1, @@ -3025,22 +3446,22 @@ var site = { }); }, - set_cli_version: function() { + set_cli_version: function () { var php_version = $("select[name='php_version']").val(); var loading = bt.load(); - $.post('/config?action=set_cli_php_version', { php_version: php_version }, function(rdata) { + $.post('/config?action=set_cli_php_version', {php_version: php_version}, function (rdata) { loading.close(); if (rdata.status) { layer.closeAll(); } - layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); + layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); }); }, - del_site: function(wid, wname,callback) { + del_site: function (wid, wname, callback) { var num1 = bt.get_random_num(1, 9), num2 = bt.get_random_num(1, 9), title = ''; - title = typeof wname === "function" ? 'Deleting sites in batches' : lan.site.site_del_title + ' [ '+ wname +' ]'; + title = typeof wname === "function" ? 'Deleting sites in batches' : lan.site.site_del_title + ' [ ' + wname + ' ]'; layer.open({ type: 1, title: title, @@ -3050,61 +3471,68 @@ var site = { closeBtn: 2, shadeClose: true, content: '\ -
                                      \ - \ -
                                      ' + lan.site.site_del_info + '
                                      \ -
                                      \ - \ - \ - \ -
                                      \ -
                                      \ - ' + lan.bt.cal_msg + '' + num1 + ' + ' + num2 + '=\ - \ -
                                      \ -
                                      \ - ', - btn:[lan.public.ok, lan.public.cancel], +
                                      \ + \ +
                                      ' + lan.site.site_del_info + '
                                      \ +
                                      \ + \ + \ + \ +
                                      \ +
                                      \ + ' + lan.bt.cal_msg + '' + num1 + ' + ' + num2 + '=\ + \ +
                                      \ +
                                      \ + ', + btn: [lan.public.ok, lan.public.cancel], success: function (layers, indexs) { - $(layers).find('.check_type_group label').hover(function(){ + $(layers).find('.check_type_group label').hover(function () { var name = $(this).find('input').attr('name'); if (name === 'data' && !recycle_bin_db_open) { - layer.tips('Risky operation: the current database recycle bin is not open, delete the database will disappear forever!', this, {tips: [1, 'red'], time: 0}); + layer.tips('Risky operation: the current database recycle bin is not open, delete the database will disappear forever!', this, { + tips: [1, 'red'], + time: 0 + }); } else if (name === 'path' && !recycle_bin_open) { - layer.tips('Risky operation: The current file recycle bin is not open, delete the site directory will disappear forever!', this, {tips: [1, 'red'], time: 0 }); + layer.tips('Risky operation: The current file recycle bin is not open, delete the site directory will disappear forever!', this, { + tips: [1, 'red'], + time: 0 + }); } - }, function() { + }, function () { layer.closeAll('tips'); }); }, yes: function (indexs) { - var vcodeResult = $('#vcodeResult'),data = {id: wid,webname: wname}; - $('#site_delete_form input[type=checkbox]').each(function(index,item){ - if($(item).is(':checked')) data[$(item).attr('name')] = 1 + var vcodeResult = $('#vcodeResult'), data = {id: wid, webname: wname}; + $('#site_delete_form input[type=checkbox]').each(function (index, item) { + if ($(item).is(':checked')) data[$(item).attr('name')] = 1 }) - if(vcodeResult.val() === ''){ - layer.tips('The result cannot be null', vcodeResult, {tips: [1, 'red'],time:3000}) + if (vcodeResult.val() === '') { + layer.tips('The result cannot be null', vcodeResult, {tips: [1, 'red'], time: 3000}) vcodeResult.focus() return false; - }else if(parseInt(vcodeResult.val()) !== (num1 + num2)){ - layer.tips('The calculation is incorrect', vcodeResult, {tips: [1, 'red'],time:3000}) + } else if (parseInt(vcodeResult.val()) !== (num1 + num2)) { + layer.tips('The calculation is incorrect', vcodeResult, {tips: [1, 'red'], time: 3000}) vcodeResult.focus() return false; } - var is_database = data.hasOwnProperty('database'),is_path = data.hasOwnProperty('path'),is_ftp = data.hasOwnProperty('ftp'); - if((!is_database && !is_path) && (!is_ftp || is_ftp)){ - if(typeof wname === "function"){ + var is_database = data.hasOwnProperty('database'), is_path = data.hasOwnProperty('path'), + is_ftp = data.hasOwnProperty('ftp'); + if ((!is_database && !is_path) && (!is_ftp || is_ftp)) { + if (typeof wname === "function") { wname(data) return false; } @@ -3115,19 +3543,20 @@ var site = { }) return false } - if(typeof wname === "function"){ + if (typeof wname === "function") { delete data.id; delete data.webname; } layer.close(indexs) - var ids = JSON.stringify(wid instanceof Array ? wid : [ wid ]),countDown = typeof wname === 'string'?4:9; - title = typeof wname === "function" ?'Verify the information twice and delete sites in batches':'Verify information twice, delete site [ ' + wname + ' ]'; + var ids = JSON.stringify(wid instanceof Array ? wid : [wid]), + countDown = typeof wname === 'string' ? 4 : 9; + title = typeof wname === "function" ? 'Verify the information twice and delete sites in batches' : 'Verify information twice, delete site [ ' + wname + ' ]'; var loadT = bt.load('Checking site data information, please wait...') bt.send('check_del_data', 'site/check_del_data', {ids: ids}, function (res) { loadT.close() layer.open({ - type:1, - title:title, + type: 1, + title: title, closeBtn: 2, skin: 'verify_site_layer_info active', area: '740px', @@ -3140,7 +3569,7 @@ var site = { '
                                      ' + '' + '' + - '
                                      Risks: The database recycle bin function is not enabled at present. After the database is deleted, the database will disappear forever!
                                      ' + + '
                                      Risks: The database recycle bin function is not enabled at present. After the database is deleted, the database will disappear forever!
                                      ' + '
                                      Risk: The file recycle bin function is disabled at present. After a site directory is deleted, the site directory will disappear forever!
                                      ' + '
                                      Please read the above information to be deleted carefully to prevent site data from being deleted by mistake. Confirm that there are still ' + countDown + ' seconds left to delete.
                                      ' + '', @@ -3151,25 +3580,25 @@ var site = { var item = rdata[i], newTime = parseInt(new Date().getTime() / 1000), t_icon = ''; - site_html = (function(item){ - if(!is_path) return '' + site_html = (function (item) { + if (!is_path) return '' var is_time_rule = (newTime - item.st_time) > (86400 * 30) && (item.total > 1024 * 10), is_path_rule = res.file_size <= item.total, dir_time = bt.format_data(item.st_time, 'yyyy-MM-dd'), dir_size = bt.format_size(item.total); - var f_html = ' ' +(item.limit ? 'More than 50 MB' : dir_size) + ' ' + (is_path_rule ? t_icon : ''); - var f_title = (is_path_rule ?'Note: This directory may contain important data. Exercise caution when performing this operation.\n':'') + 'directory:' + item.path + '(' + (item.limit ? 'greater than ' : '') + dir_size + ')'; + var f_html = ' ' + (item.limit ? 'More than 50 MB' : dir_size) + ' ' + (is_path_rule ? t_icon : ''); + var f_title = (is_path_rule ? 'Note: This directory may contain important data. Exercise caution when performing this operation.\n' : '') + 'directory:' + item.path + '(' + (item.limit ? 'greater than ' : '') + dir_size + ')'; return '
                                      ' + 'Site: ' + item.name + '' + 'Path: ' + item.path + ' (' + f_html + ')' + - 'Create: ' + dir_time + '' + + 'Create: ' + dir_time + '' + '
                                      ' }(item)), - database_html = (function(item){ - if(!is_database || !item.database) return ''; - var is_time_rule = (newTime - item.st_time) > (86400 * 30) && (item.total > 1024 * 10), + database_html = (function (item) { + if (!is_database || !item.database) return ''; + var is_time_rule = (newTime - item.st_time) > (86400 * 30) && (item.total > 1024 * 10), is_database_rule = res.db_size <= item.database.total, database_time = bt.format_data(item.database.st_time, 'yyyy-MM-dd'), database_size = bt.format_size(item.database.total); @@ -3179,13 +3608,13 @@ var site = { return '
                                      ' + 'DB: ' + item.database.name + '' + - 'Size: ' + f_size +'' + - 'Create: ' + database_time + '' + + 'Size: ' + f_size + '' + + 'Create: ' + database_time + '' + '
                                      ' }(item)) - if((site_html + database_html) !== '') html += '
                                      ' + site_html + database_html +'
                                      '; + if ((site_html + database_html) !== '') html += '
                                      ' + site_html + database_html + '
                                      '; } - if(html === '') html = '
                                      No data
                                      ' + if (html === '') html = '
                                      No data
                                      ' $('.check_layer_content').html(html) var interVal = setInterval(function () { countDown--; @@ -3199,14 +3628,17 @@ var site = { clearInterval(interVal) }, countDown * 1000) }, - yes:function(indes,layers){ - if($(layers).hasClass('active')){ - layer.tips('Please confirm the information and try again later. ' + countDown + ' seconds left', $(layers).find('.layui-layer-btn0') , {tips: [1, 'red'],time:3000}) + yes: function (indes, layers) { + if ($(layers).hasClass('active')) { + layer.tips('Please confirm the information and try again later. ' + countDown + ' seconds left', $(layers).find('.layui-layer-btn0'), { + tips: [1, 'red'], + time: 3000 + }) return; } - if(typeof wname === "function"){ + if (typeof wname === "function") { wname(data) - }else{ + } else { bt.site.del_site(data, function (rdata) { layer.closeAll() if (rdata.status) site.get_list(); @@ -3220,29 +3652,38 @@ var site = { } }); }, - batch_site: function(type, obj, result) { + batch_site: function (type, obj, result) { if (obj == undefined) { obj = {}; var arr = []; - result = { count: 0, error_list: [] }; - $('input[type="checkbox"].check:checked').each(function() { + result = {count: 0, error_list: []}; + $('input[type="checkbox"].check:checked').each(function () { var _val = $(this).val(); if (!isNaN(_val)) arr.push($(this).parents('tr').data('item')); }) if (type == 'site_type') { - bt.site.get_type(function(tdata) { + bt.site.get_type(function (tdata) { var types = []; - for (var i = 0; i < tdata.length; i++) types.push({ title: tdata[i].name, value: tdata[i].id }) + for (var i = 0; i < tdata.length; i++) types.push({title: tdata[i].name, value: tdata[i].id}) var form = { title: lan.site.set_site_classification, area: '530px', - list: [{ title: lan.site.default_site, name: 'type_id', width: '300px', type: 'select', items: types }], + list: [{ + title: lan.site.default_site, + name: 'type_id', + width: '300px', + type: 'select', + items: types + }], btns: [ bt.form.btn.close(), - bt.form.btn.submit(lan.site.submit, function(rdata, load) { + bt.form.btn.submit(lan.site.submit, function (rdata, load) { var ids = [] for (var x = 0; x < arr.length; x++) ids.push(arr[x].id); - bt.site.set_site_type({ id: rdata.type_id, site_array: JSON.stringify(ids) }, function(rrdata) { + bt.site.set_site_type({ + id: rdata.type_id, + site_array: JSON.stringify(ids) + }, function (rrdata) { if (rrdata.status) { load.close(); site.get_list(); @@ -3257,7 +3698,7 @@ var site = { return; } var thtml = "
                                      "; - bt.show_confirm(lan.site.all_del_site, "" + lan.get('del_all_site', [arr.length]) + "", function() { + bt.show_confirm(lan.site.all_del_site, "" + lan.get('del_all_site', [arr.length]) + "", function () { if ($("#delpath").is(":checked")) obj.path = '1'; obj.data = arr; bt.closeAll(); @@ -3271,15 +3712,15 @@ var site = { case 'del': if (obj.data.length < 1) { site.get_list(); - bt.msg({ msg: lan.get('del_all_site_ok', [result.count]), icon: 1, time: 5000 }); + bt.msg({msg: lan.get('del_all_site_ok', [result.count]), icon: 1, time: 5000}); return; } - var data = { id: item.id, webname: item.name, path: obj.path } - bt.site.del_site(data, function(rdata) { + var data = {id: item.id, webname: item.name, path: obj.path} + bt.site.del_site(data, function (rdata) { if (rdata.status) { result.count += 1; } else { - result.error_list.push({ name: item.item, err_msg: rdata.msg }); + result.error_list.push({name: item.item, err_msg: rdata.msg}); } obj.data.splice(0, 1) site.batch_site(type, obj, result); @@ -3288,17 +3729,17 @@ var site = { } }, - set_class_type: function() { + set_class_type: function () { var _form_data = bt.render_form_line({ title: '', items: [ - { placeholder: lan.site.input_classification_name, name: 'type_name', width: '50%', type: 'text' }, + {placeholder: lan.site.input_classification_name, name: 'type_name', width: '50%', type: 'text'}, { name: 'btn_submit', text: lan.site.add, type: 'button', - callback: function(sdata) { - bt.site.add_type(sdata.type_name, function(ldata) { + callback: function (sdata) { + bt.site.add_type(sdata.type_name, function (ldata) { if (ldata.status) { $('[name="type_name"]').val(''); site.get_class_type(); @@ -3317,18 +3758,21 @@ var site = { shift: 5, shadeClose: true, content: "
                                      " + _form_data.html + "
                                      ", - success: function() { + success: function () { bt.render_clicks(_form_data.clicks); - site.get_class_type(function(res) { - $('#type_table').on('click', '.del_type', function() { + site.get_class_type(function (res) { + $('#type_table').on('click', '.del_type', function () { var _this = $(this); var item = _this.parents('tr').data('item'); if (item.id == 0) { - bt.msg({ icon: 2, msg: lan.site.default_classification_cant_operation }); + bt.msg({icon: 2, msg: lan.site.default_classification_cant_operation}); return; } - bt.confirm({ msg: lan.site.sure_del_classification, title: lan.site.del_classification + '【' + item.name + '】' }, function() { - bt.site.del_type(item.id, function(ret) { + bt.confirm({ + msg: lan.site.sure_del_classification, + title: lan.site.del_classification + '【' + item.name + '】' + }, function () { + bt.site.del_type(item.id, function (ret) { if (ret.status) { site.get_class_type(); bt.set_cookie('site_type', '-1'); @@ -3337,24 +3781,29 @@ var site = { }) }) }); - $('#type_table').on('click', '.edit_type', function() { + $('#type_table').on('click', '.edit_type', function () { var item = $(this).parents('tr').data('item'); if (item.id == 0) { - bt.msg({ icon: 2, msg: lan.site.default_classification_cant_operation }); + bt.msg({icon: 2, msg: lan.site.default_classification_cant_operation}); return; } bt.render_form({ title: lan.site.edit_classification_mam + '【' + item.name + '】', area: '350px', - list: [{ title: lan.site.classification_name, width: '150px', name: 'name', value: item.name }], + list: [{ + title: lan.site.classification_name, + width: '150px', + name: 'name', + value: item.name + }], btns: [ - { title: lan.site.turn_off, name: 'close' }, + {title: lan.site.turn_off, name: 'close'}, { title: lan.site.submit, name: 'submit', css: 'btn-success', - callback: function(rdata, load, callback) { - bt.site.edit_type({ id: item.id, name: rdata.name }, function(edata) { + callback: function (rdata, load, callback) { + bt.site.edit_type({id: item.id, name: rdata.name}, function (edata) { if (edata.status) { load.close(); site.get_class_type(); @@ -3370,17 +3819,24 @@ var site = { } }); }, - get_class_type: function(callback) { - site.get_types(function(rdata) { + get_class_type: function (callback) { + site.get_types(function (rdata) { bt.render({ table: '#type_table', columns: [ - { field: 'name', title: lan.site.name }, - { field: 'opt', width: '80px', title: lan.site.operate, templet: function(item) { return '' + lan.site.edit + ' | ' + lan.site.del + ''; } } + {field: 'name', title: lan.site.name}, + { + field: 'opt', width: '80px', title: lan.site.operate, templet: function (item) { + return '' + lan.site.edit + ' | ' + lan.site.del + ''; + } + } ], data: rdata }); - $('.layui-layer-page').css({ 'margin-top': '-' + ($('.layui-layer-page').height() / 2) + 'px', 'top': '50%' }); + $('.layui-layer-page').css({ + 'margin-top': '-' + ($('.layui-layer-page').height() / 2) + 'px', + 'top': '50%' + }); if (callback) callback(rdata); }); }, @@ -3388,14 +3844,14 @@ var site = { my_ssl_msg: null, //续签订单内 - renew_ssl: function(siteName, auth_type, index) { + renew_ssl: function (siteName, auth_type, index) { acme.siteName = siteName; if (index.length === 32 && index.indexOf('/') === -1) { - acme.renew(index, function(rdata) { + acme.renew(index, function (rdata) { site.ssl.ssl_result(rdata, auth_type, siteName) }); } else { - acme.get_cert_init(index, siteName, function(cert_init) { + acme.get_cert_init(index, siteName, function (cert_init) { acme.domains = cert_init.dns; var options = ''; for (var i = 0; i < cert_init.dnsapi.length; i++) { @@ -3409,24 +3865,24 @@ var site = { area: "500px", offset: "30%", content: '
                                      \ -
                                      \ -
                                      Please select a verification method:
                                      \ -
                                      \ - \ - \ - \ -
                                      \ -
                                      \ -
                                        \ -
                                      • Wildcard certificate cannot use [File Authentication], please select DNS authentication
                                      • \ -
                                      • Use [File Authentication], please make sure that [Enable HTTPS / 301 Redirect / Reverse Proxy] and other functions are not enabled.
                                      • \ -
                                      • Use [Alibaba Cloud DNS] [DnsPod] and other authentication methods to set the correct key
                                      • \ -
                                      • After the renewal is successful, the certificate will try to renew automatically 30 days before the next expiration
                                      • \ -
                                      • Using [DNS Authentication-Manual Resolution] Renewed certificate cannot be automatically renewed 30 days before the next expiration
                                      • \ -
                                      \ -
                                      ', - success: function(layers) { - $("select[name='auth_to']").change(function() { +
                                      \ +
                                      Please select a verification method:
                                      \ +
                                      \ + \ + \ + \ +
                                      \ +
                                      \ +
                                        \ +
                                      • Wildcard certificate cannot use [File Authentication], please select DNS authentication
                                      • \ +
                                      • Use [File Authentication], please make sure that [Enable HTTPS / 301 Redirect / Reverse Proxy] and other functions are not enabled.
                                      • \ +
                                      • Use [Alibaba Cloud DNS] [DnsPod] and other authentication methods to set the correct key
                                      • \ +
                                      • After the renewal is successful, the certificate will try to renew automatically 30 days before the next expiration
                                      • \ +
                                      • Using [DNS Authentication-Manual Resolution] Renewed certificate cannot be automatically renewed 30 days before the next expiration
                                      • \ +
                                      \ + ', + success: function (layers) { + $("select[name='auth_to']").change(function () { var dnsapi = $(this).val(); $(".dnsapi-btn").html(''); for (var i = 0; i < cert_init.dnsapi.length; i++) { @@ -3444,22 +3900,22 @@ var site = { } }, //续签其它 - renew_ssl_other: function() { + renew_ssl_other: function () { var auth_to = $("select[name='auth_to']").val() var auth_type = 'http' if (auth_to === 'http') { if (JSON.stringify(acme.domains).indexOf('*.') !== -1) { - layer.msg("Domain names containing wildcards cannot use File Authentication (HTTP)!", { icon: 2 }); + layer.msg("Domain names containing wildcards cannot use File Authentication (HTTP)!", {icon: 2}); return; } auth_to = acme.id } else { if (auth_to !== 'dns') { if (auth_to === "Dns_com") { - acme.dnsapi.data = [{ value: "None" }, { value: "None" }]; + acme.dnsapi.data = [{value: "None"}, {value: "None"}]; } if (!acme.dnsapi.data[0].value || !acme.dnsapi.data[1].value) { - layer.msg("Please set [" + acme.dnsapi.title + "] interface information first!", { icon: 2 }); + layer.msg("Please set [" + acme.dnsapi.title + "] interface information first!", {icon: 2}); return; } auth_to = auth_to + '|' + acme.dnsapi.data[0].value + '|' + acme.dnsapi.data[1].value; @@ -3467,11 +3923,11 @@ var site = { auth_type = 'dns' } layer.close(acme.select_loadT); - acme.apply_cert(acme.domains, auth_type, auth_to, '0', function(rdata) { + acme.apply_cert(acme.domains, auth_type, auth_to, '0', function (rdata) { site.ssl.ssl_result(rdata, auth_type, acme.siteName); }); }, - show_dnsapi_setup: function() { + show_dnsapi_setup: function () { var dnsapi = acme.dnsapi; acme.dnsapi_loadT = layer.open({ title: 'Set [' + dnsapi.title + '] interface', @@ -3481,28 +3937,28 @@ var site = { area: "550px", offset: "30%", content: '
                                      \ -
                                      \ - ' + dnsapi.data[0].key + '\ -
                                      \ - \ -
                                      \ -
                                      \ -
                                      \ - ' + dnsapi.data[1].key + '\ -
                                      \ - \ -
                                      \ -
                                      \ -
                                      \ - \ - \ -
                                      \ -
                                        \ -
                                      • ' + dnsapi.help + '
                                      • \ -
                                      \ -
                                      ', - success: function(layers) { - $(".dnsapi-save").click(function() { +
                                      \ + ' + dnsapi.data[0].key + '\ +
                                      \ + \ +
                                      \ +
                                      \ +
                                      \ + ' + dnsapi.data[1].key + '\ +
                                      \ + \ +
                                      \ +
                                      \ +
                                      \ + \ + \ +
                                      \ +
                                        \ +
                                      • ' + dnsapi.help + '
                                      • \ +
                                      \ + ', + success: function (layers) { + $(".dnsapi-save").click(function () { var dnsapi_key = $(".dnsapi-key"); var dnsapi_token = $(".dnsapi-token"); pdata = {} @@ -3510,7 +3966,7 @@ var site = { pdata[dnsapi_token.attr("name")] = dnsapi_token.val(); acme.dnsapi.data[0].value = dnsapi_key.val(); acme.dnsapi.data[1].value = dnsapi_token.val(); - bt.site.set_dns_api({ pdata: JSON.stringify(pdata) }, function(ret) { + bt.site.set_dns_api({pdata: JSON.stringify(pdata)}, function (ret) { if (ret.status) layer.close(acme.dnsapi_loadT); bt.msg(ret); }); @@ -3518,7 +3974,7 @@ var site = { } }); }, - set_cert: function(siteName, res) { + set_cert: function (siteName, res) { var loadT = bt.load(lan.site.saving_txt); var pdata = { type: 1, @@ -3526,13 +3982,13 @@ var site = { key: res.private_key, csr: res.cert + res.root } - bt.send('SetSSL', 'site/SetSSL', pdata, function(rdata) { + bt.send('SetSSL', 'site/SetSSL', pdata, function (rdata) { loadT.close(); site.reload(); - layer.msg(res.msg, { icon: 1 }); + layer.msg(res.msg, {icon: 1}); }) }, - show_error: function(res, auth_type) { + show_error: function (res, auth_type) { var area_size = '500px'; var err_info = ""; if (res.msg[1].challenges === undefined) { @@ -3583,9 +4039,9 @@ var site = { area: area_size }); }, - ssl_result: function(res, auth_type, siteName) { + ssl_result: function (res, auth_type, siteName) { layer.close(acme.loadT); - if (res.status === false && typeof(res.msg) === 'string') { + if (res.status === false && typeof (res.msg) === 'string') { bt.msg(res); return; } @@ -3599,17 +4055,17 @@ var site = { shift: 5, shadeClose: false, content: "
                                      \ -

                                      Please do TXT analysis according to the following list:

                                      \ -
                                      \ -
                                      \ - \ -
                                      \ -
                                      " +

                                      Please do TXT analysis according to the following list:

                                      \ +
                                      \ +
                                      \ + \ +
                                      \ + " }); //手动验证事件 - $('.btn_check_txt').click(function() { - acme.auth_domain(res.index, function(res1) { + $('.btn_check_txt').click(function () { + acme.auth_domain(res.index, function (res1) { layer.close(acme.loadT); if (res1.status === true) { b_load.close() @@ -3622,7 +4078,7 @@ var site = { }); //显示手动验证信息 - setTimeout(function() { + setTimeout(function () { var data = []; acme_txt = '_acme-challenge.' for (var j = 0; j < res.auths.length; j++) { @@ -3642,10 +4098,10 @@ var site = { bt.render({ table: '#dns_txt_jx', columns: [ - { field: 'name', width: '220px', title: 'Resolving domain names' }, - { field: 'txt', title: 'Record value' }, - { field: 'type', title: 'Types of' }, - { field: 'force', title: 'essential' } + {field: 'name', width: '220px', title: 'Resolving domain names'}, + {field: 'txt', title: 'Record value'}, + {field: 'type', title: 'Types of'}, + {field: 'force', title: 'essential'} ], data: data }) @@ -3663,8 +4119,8 @@ var site = { site.ssl.show_error(res, auth_type); }, - get_renew_stat: function() { - $.post('/ssl?action=Get_Renew_SSL', {}, function(task_list) { + get_renew_stat: function () { + $.post('/ssl?action=Get_Renew_SSL', {}, function (task_list) { if (!task_list.status) return; var s_body = ''; var b_stat = false; @@ -3678,24 +4134,30 @@ var site = { if (site.ssl.my_ssl_msg) { $(".my-renew-ssl").html(s_body); } else { - site.ssl.my_ssl_msg = layer.msg('
                                      ' + s_body + '
                                      ', { time: 0, icon: 16, shade: 0.3 }); + site.ssl.my_ssl_msg = layer.msg('
                                      ' + s_body + '
                                      ', { + time: 0, + icon: 16, + shade: 0.3 + }); } if (!b_stat) { - setTimeout(function() { + setTimeout(function () { layer.close(site.ssl.my_ssl_msg); site.ssl.my_ssl_msg = null; }, 3000); return; } - setTimeout(function() { site.ssl.get_renew_stat(); }, 1000); + setTimeout(function () { + site.ssl.get_renew_stat(); + }, 1000); }); }, - onekey_ssl: function(partnerOrderId, siteName) { - bt.site.get_ssl_info(partnerOrderId, siteName, function(rdata) { + onekey_ssl: function (partnerOrderId, siteName) { + bt.site.get_ssl_info(partnerOrderId, siteName, function (rdata) { bt.msg(rdata); if (rdata.status) site.reload(7); }) @@ -3705,19 +4167,19 @@ var site = { bt.msg(rdata); if (rdata.status) { site.reload(7); - if(ssl_id != undefined){ - setTimeout(function(){ - $('#ssl_tabs span:eq('+ ssl_id +')').click(); - },1000) + if (ssl_id != undefined) { + setTimeout(function () { + $('#ssl_tabs span:eq(' + ssl_id + ')').click(); + }, 1000) } if (action == 'CloseSSLConf') { - layer.msg(lan.site.ssl_close_info, { icon: 1, time: 5000 }); + layer.msg(lan.site.ssl_close_info, {icon: 1, time: 5000}); } } }) }, - verify_domain: function(partnerOrderId, siteName) { - bt.site.verify_domain(partnerOrderId, siteName, function(vdata) { + verify_domain: function (partnerOrderId, siteName) { + bt.site.verify_domain(partnerOrderId, siteName, function (vdata) { bt.msg(vdata); if (vdata.status) { if (vdata.data.stateCode == 'COMPLETED') { @@ -3729,276 +4191,303 @@ var site = { } }) }, - reload: function(index) { + reload: function (index) { if (index == undefined) index = 0 var _sel = $('#ssl_tabs .on'); if (_sel.length == 0) _sel = $('#ssl_tabs span:eq(0)'); _sel.trigger('click'); + }, + set_auto_restart_rph: function (sitename) { + var $checkbox = $('#auto_restart_rph'); + var checked = $checkbox.is(':checked'); + var url = checked ? 'remove_auto_restart_rph' : 'auto_restart_rph'; + var loadT = bt.load(lan.site.the_msg); + $.post('/site?action=' + url, { + sitename: sitename + }, function (res) { + loadT.close(); + bt.msg(res); + if (res.status) { + $checkbox.prop('checked', !checked); + } + }); } }, edit: { - update_composer: function () { - loadT = bt.load() - $.post('/files?action=update_composer', { - repo: $("select[name='repo']").val() - }, function (v_data) { - loadT.close(); - bt.msg(v_data); - }); + update_composer: function () { + loadT = bt.load() + $.post('/files?action=update_composer', { + repo: $("select[name='repo']").val() + }, function (v_data) { + loadT.close(); + bt.msg(v_data); + }); }, show_composer_log: function () { - $.post('/ajax?action=get_lines', { - filename: '/tmp/composer.log', - num: 30 - }, function (v_body) { - var log_obj = $("#composer-log") - if (log_obj.length < 1) return; - log_obj.html(v_body.msg); - var div = document.getElementById('composer-log') - div.scrollTop = div.scrollHeight; - if (v_body.msg.indexOf('BT-Exec-Completed') != -1) { - //layer.close(site.edit.comp_showlog); - layer.msg('Execution complete', { - icon: 1 - }); - return; - } + $.post('/ajax?action=get_lines', { + filename: '/tmp/composer.log', + num: 30 + }, function (v_body) { + var log_obj = $("#composer-log") + if (log_obj.length < 1) return; + log_obj.html(v_body.msg); + var div = document.getElementById('composer-log') + div.scrollTop = div.scrollHeight; + if (v_body.msg.indexOf('BT-Exec-Completed') != -1) { + //layer.close(site.edit.comp_showlog); + layer.msg('Execution complete', { + icon: 1 + }); + return; + } - setTimeout(function () { - site.edit.show_composer_log() - }, 1000) - }); + setTimeout(function () { + site.edit.show_composer_log() + }, 1000) + }); }, comp_confirm: 0, comp_showlog: 0, exec_composer: function () { - site.edit.comp_confirm = layer.confirm('The impact of Composer execution depends on the composer.json configuration file in this directory. Continue?', { - title: 'Execute composer', - closeBtn: 2, - icon: 3 - }, function (index) { - layer.close(site.edit.comp_confirm); - var pdata = { - php_version: $("select[name='php_version']").val(), - composer_args: $("select[name='composer_args']").val(), - composer_cmd: $("input[name='composer_cmd']").val(), - repo: $("select[name='repo']").val(), - path: $("input[name='composer_path']").val(), - user: $("select[name='composer_user']").val() - } - $.post('/files?action=exec_composer', pdata, function (rdatas) { - if (!rdatas.status) { - layer.msg(rdatas.msg, { - icon: 2 + site.edit.comp_confirm = layer.confirm('The impact of Composer execution depends on the composer.json configuration file in this directory. Continue?', { + title: 'Execute composer', + closeBtn: 2, + icon: 3 + }, function (index) { + layer.close(site.edit.comp_confirm); + var pdata = { + php_version: $("select[name='php_version']").val(), + composer_args: $("select[name='composer_args']").val(), + composer_cmd: $("input[name='composer_cmd']").val(), + repo: $("select[name='repo']").val(), + path: $("input[name='composer_path']").val(), + user: $("select[name='composer_user']").val() + } + $.post('/files?action=exec_composer', pdata, function (rdatas) { + if (!rdatas.status) { + layer.msg(rdatas.msg, { + icon: 2 + }); + return false; + } + if (rdatas.status === true) { + site.edit.comp_showlog = layer.open({ + area: "800px", + type: 1, + shift: 5, + closeBtn: 2, + title: 'Execute Composer in the [' + pdata['path'] + '] directory. After execution, please close this window after confirming that there is no problem', + content: "
                                      "
                                      +                        });
                                      +                        setTimeout(function () {
                                      +                            site.edit.show_composer_log();
                                      +                        }, 200);
                                      +                    }
                                                       });
                                      -                return false;
                                      -              }
                                      -              if (rdatas.status === true) {
                                      -                site.edit.comp_showlog = layer.open({
                                      -                  area: "800px",
                                      -                  type: 1,
                                      -                  shift: 5,
                                      -                  closeBtn: 2,
                                      -                  title: 'Execute Composer in the ['+ pdata['path'] +'] directory. After execution, please close this window after confirming that there is no problem',
                                      -                  content: "
                                      "
                                      -                });
                                      -                setTimeout(function () {
                                      -                  site.edit.show_composer_log();
                                      -                }, 200);
                                      -              }
                                                   });
                                      -          });
                                               },
                                               remove_composer_lock: function (path) {
                                      -          $.post('/files?action=DeleteFile', {
                                      -            path: path + '/composer.lock'
                                      -          }, function (rdata) {
                                      -            bt.msg(rdata);
                                      -            $(".composer-msg").remove();
                                      -            $(".composer-rm").remove();
                                      -          })
                                      +            $.post('/files?action=DeleteFile', {
                                      +                path: path + '/composer.lock'
                                      +            }, function (rdata) {
                                      +                bt.msg(rdata);
                                      +                $(".composer-msg").remove();
                                      +                $(".composer-rm").remove();
                                      +            })
                                               },
                                               set_composer: function (web) {
                                      -          $.post('/files?action=get_composer_version', {
                                      -            path: web.path
                                      -          }, function (v_data) {
                                      -            if (v_data.status === false) {
                                      -              bt.msg(v_data);
                                      -              return;
                                      -            }
                                      +            $.post('/files?action=get_composer_version', {
                                      +                path: web.path
                                      +            }, function (v_data) {
                                      +                if (v_data.status === false) {
                                      +                    bt.msg(v_data);
                                      +                    return;
                                      +                }
                                       
                                      -            var php_versions = '';
                                      -            for (var i = 0; i < v_data.php_versions.length; i++) {
                                      -              if (v_data.php_versions[i].version == '00') continue;
                                      -              php_versions += '';
                                      -            }
                                      +                var php_versions = '';
                                      +                for (var i = 0; i < v_data.php_versions.length; i++) {
                                      +                    if (v_data.php_versions[i].version == '00') continue;
                                      +                    php_versions += '';
                                      +                }
                                       
                                      -            var msg = '';
                                      -            if (v_data.comp_lock) {
                                      -              msg += '' + v_data.comp_lock + ' [Delete]'
                                      -            }
                                      -            if (v_data.comp_json !== true) {
                                      -              msg += '' + v_data.comp_json + ''
                                      -            }
                                      +                var msg = '';
                                      +                if (v_data.comp_lock) {
                                      +                    msg += '' + v_data.comp_lock + ' [Delete]'
                                      +                }
                                      +                if (v_data.comp_json !== true) {
                                      +                    msg += '' + v_data.comp_json + ''
                                      +                }
                                       
                                      -            var com_body = '' +
                                      -              '
                                      Version
                                      ' + - '
                                      PHP
                                      ' + - '' + - '
                                      ' + - '
                                      Parameters
                                      ' + - '' + - '
                                      ' + + var com_body = '' + + '
                                      Version
                                      ' + + '
                                      PHP
                                      ' + + '' + + '
                                      ' + + '
                                      Parameters
                                      ' + + '' + + '
                                      ' + - '
                                      Extra commands
                                      ' + - '' + - '
                                      ' + + '
                                      Extra commands
                                      ' + + '' + + '
                                      ' + - '
                                      Source
                                      ' + - '' + - '
                                      ' + - '
                                      User
                                      ' + - '' + - '
                                      ' + - '
                                      Dir
                                      ' + - '' + - '
                                      ' + - '
                                      ' + msg + '
                                      ' + - '
                                      ' + - '
                                      ' + - '
                                        ' + - '
                                      • Directory:Website root dir by default, please make sure that the dir contains composer.json
                                      • ' + - '
                                      • User:The default user www, unless your website is run with root privileges, it is not recommended to use the root user to execute composer
                                      • ' + - '
                                      • Source:source of composer
                                      • ' + - '
                                      • Parameters:Install (install dependent package), Update (upgrade dependent package), please select as needed
                                      • ' + - '
                                      • Extra commands: If this is empty, it will be executed according to the conf in composer.json, Supported fill in the complete composer command
                                      • ' + - '
                                      • PHP version:The PHP version used to execute composer, it is recommended to try the default, if the installation fails, try to choose another PHP version
                                      • ' + - '
                                      • Composer version:Composer version, you can click [Upgrade Composer] on the right to upgrade Composer to the latest stable version
                                      • ' + - '
                                      ' - $("#webedit-con").html(com_body); - }); + '
                                      Source
                                      ' + + '' + + '
                                      ' + + '
                                      User
                                      ' + + '' + + '
                                      ' + + '
                                      Dir
                                      ' + + '' + + '
                                      ' + + '
                                      ' + msg + '
                                      ' + + '
                                      ' + + '
                                      ' + + '
                                        ' + + '
                                      • Directory:Website root dir by default, please make sure that the dir contains composer.json
                                      • ' + + '
                                      • User:The default user www, unless your website is run with root privileges, it is not recommended to use the root user to execute composer
                                      • ' + + '
                                      • Source:source of composer
                                      • ' + + '
                                      • Parameters:Install (install dependent package), Update (upgrade dependent package), please select as needed
                                      • ' + + '
                                      • Extra commands: If this is empty, it will be executed according to the conf in composer.json, Supported fill in the complete composer command
                                      • ' + + '
                                      • PHP version:The PHP version used to execute composer, it is recommended to try the default, if the installation fails, try to choose another PHP version
                                      • ' + + '
                                      • Composer version:Composer version, you can click [Upgrade Composer] on the right to upgrade Composer to the latest stable version
                                      • ' + + '
                                      ' + $("#webedit-con").html(com_body); + }); }, - set_domains: function(web) { + set_domains: function (web) { var _this = this; - var list = [{ - items: [ - { name: 'newdomain', width: '400px', type: 'textarea', placeholder: lan.site.domain_help }, - { - name: 'btn_submit_domain', - text: lan.site.add, - type: 'button', - callback: function(sdata) { - var arrs = sdata.newdomain.split("\n"); - var domins = ""; - for (var i = 0; i < arrs.length; i++) domins += arrs[i] + ","; - bt.site.add_domains(web.id, web.name, bt.rtrim(domins, ','), function(ret) { - if (ret.status) site.reload(0) - }) - } + var list = [{ + items: [ + {name: 'newdomain', width: '400px', type: 'textarea', placeholder: lan.site.domain_help}, + { + name: 'btn_submit_domain', + text: lan.site.add, + type: 'button', + callback: function (sdata) { + var arrs = sdata.newdomain.split("\n"); + var domins = ""; + for (var i = 0; i < arrs.length; i++) domins += arrs[i] + ","; + bt.site.add_domains(web.id, web.name, bt.rtrim(domins, ','), function (ret) { + if (ret.status) site.reload(0) + }) } - ] - }] - var _form_data = bt.render_form_line(list[0]),loadT = null,placeholder = null; - $('#webedit-con').html(_form_data.html + "
                                      "); - bt.render_clicks(_form_data.clicks); - $('.btn_submit_domain').addClass('pull-right').css("margin", "30px 35px 0 0"); - placeholder = $(".placeholder"); - placeholder.click(function () { - $(this).hide(); - $('.newdomain').focus(); - }).css({ 'width':'340px', 'heigth':'100px','left': '0px', 'top': '0px', 'padding-top': '10px','padding-left': '15px'}) - $('.newdomain').focus(function(){ - placeholder.hide(); - console.log(placeholder) - loadT = layer.tips(placeholder.html(),$(this),{tips:[1,'#20a53a'],time:0,area:$(this).width()}); - }).blur(function(){ - if($(this).val().length == 0) placeholder.show(); - layer.close(loadT); - }); + } + ] + }] + var _form_data = bt.render_form_line(list[0]), loadT = null, placeholder = null; + $('#webedit-con').html(_form_data.html + "
                                      "); + bt.render_clicks(_form_data.clicks); + $('.btn_submit_domain').addClass('pull-right').css("margin", "30px 35px 0 0"); + placeholder = $(".placeholder"); + placeholder.click(function () { + $(this).hide(); + $('.newdomain').focus(); + }).css({ + 'width': '340px', + 'heigth': '100px', + 'left': '0px', + 'top': '0px', + 'padding-top': '10px', + 'padding-left': '15px' + }) + $('.newdomain').focus(function () { + placeholder.hide(); + loadT = layer.tips(placeholder.html(), $(this), {tips: [1, '#20a53a'], time: 0, area: $(this).width()}); + }).blur(function () { + if ($(this).val().length == 0) placeholder.show(); + layer.close(loadT); + }); - bt_tools.table({ - el:'#domain_table', - url:'/data?action=getData', - param:{table:'domain',list:'True',search:web.id}, - dataFilter:function(res){ - return {data:res}; + bt_tools.table({ + el: '#domain_table', + url: '/data?action=getData', + param: {table: 'domain', list: 'True', search: web.id}, + dataFilter: function (res) { + return {data: res}; + }, + column: [ + {type: 'checkbox', width: 20, keepNumber: 1}, + { + fid: 'name', title: lan.site.domain, template: function (row) { + return '' + row.name + ''; + } }, - column:[ - {type:'checkbox',width:20,keepNumber:1}, - {fid:'name',title: lan.site.domain, template:function(row){ - return ''+ row.name +''; - }}, - {fid:'port',title: lan.site.port,width:50,type:'text'}, - {title:'OPT',width:80,type:'group',align:'right',group:[{ - title:'Del', - template:function(row,that){ - return that.data.length === 1?'Inoperable':'Del'; + {fid: 'port', title: lan.site.port, width: 50, type: 'text'}, + { + title: 'OPT', width: 80, type: 'group', align: 'right', group: [{ + title: 'Del', + template: function (row, that) { + return that.data.length === 1 ? 'Inoperable' : 'Del'; }, - event:function(row,index,ev,key,that){ - if(that.data.length === 1){ - bt.msg({status:false,msg:'The last domain name cannot be deleted!'}); + event: function (row, index, ev, key, that) { + if (that.data.length === 1) { + bt.msg({status: false, msg: 'The last domain name cannot be deleted!'}); return false; } - bt.confirm({title:'Delete domain【'+ row.name +'】', msg: lan.site.domain_del_confirm }, function () { - bt.site.del_domain(web.id,web.name,row.name,row.port,function(res){ - if(res.status) that.$delete_table_row(index); + bt.confirm({ + title: 'Delete domain [ ' + row.name + ' ]', + msg: lan.site.domain_del_confirm + }, function () { + bt.site.del_domain(web.id, web.name, row.name, row.port, function (res) { + if (res.status) that.$delete_table_row(index); bt.msg(res); }); }); } }] }], - tootls:[{ // 批量操作 - type:'batch', - positon:['left','bottom'], - config:{ - title:' delete', - url:'/site?action=delete_domain_multiple', - param:{id:web.id}, - paramId:'id', - paramName:'domains_id', - theadName:'Domain', - confirmVerify:false, //是否提示验证方式 - refresh: true - } - }] - }); - $('#domain_table>.divtable').css('max-height','350px'); + tootls: [{ // 批量操作 + type: 'batch', + positon: ['left', 'bottom'], + config: { + title: ' delete', + url: '/site?action=delete_domain_multiple', + param: {id: web.id}, + paramId: 'id', + paramName: 'domains_id', + theadName: 'Domain', + confirmVerify: false, //是否提示验证方式 + refresh: true + } + }] + }); + $('#domain_table>.divtable').css('max-height', '350px'); }, set_dirbind: function (web) { var _this = this; $('#webedit-con').html('
                                      '); bt_tools.table({ - el:'#sub_dir_table', - url:'/site?action=GetDirBinding', - param:{id:web.id}, - dataFilter:function(res){ - if($('#webedit-con').children().length === 2) return {data:res.binding} + el: '#sub_dir_table', + url: '/site?action=GetDirBinding', + param: {id: web.id}, + dataFilter: function (res) { + if ($('#webedit-con').children().length === 2) return {data: res.binding} var dirs = []; - for (var n = 0; n < res.dirs.length; n++) dirs.push({ title: res.dirs[n], value: res.dirs[n] }); + for (var n = 0; n < res.dirs.length; n++) dirs.push({title: res.dirs[n], value: res.dirs[n]}); var data = { - title: '',class:'mb0',items: [ - { title: lan.site.domain, width: '140px', name: 'domain'}, - { title: lan.site.subdirectories, name: 'dirName', type: 'select', items: dirs }, + title: '', class: 'mb0', items: [ + {title: lan.site.domain, width: '140px', name: 'domain'}, + {title: lan.site.subdirectories, name: 'dirName', type: 'select', items: dirs}, { text: lan.site.add, type: 'button', name: 'btn_add_subdir', callback: function (sdata) { if (!sdata.domain || !sdata.dirName) { - layer.msg(lan.site.d_s_empty, { icon: 2 }); + layer.msg(lan.site.d_s_empty, {icon: 2}); return; } bt.site.add_dirbind(web.id, sdata.domain, sdata.dirName, function (ret) { - layer.msg(ret.msg, { icon: ret.status ? 1 : 2 }); + layer.msg(ret.msg, {icon: ret.status ? 1 : 2}); if (ret.status) site.reload(1) }) } @@ -4008,169 +4497,204 @@ var site = { var _form_data = bt.render_form_line(data); $('#webedit-con').prepend(_form_data.html); bt.render_clicks(_form_data.clicks); - return {data:res.binding}; + return {data: res.binding}; }, - column:[ - {type:'checkbox',width:20,keepNumber:1}, - {fid:'domain',title:lan.site.domain,type:'text'}, - {fid:'port',title:lan.site.port,width:70,type:'text'}, - {fid:'path',title:lan.site.subdirectories,width:70,type:'text'}, - {title:'Opt',width:130,type:'group',align:'right',group:[{ - title:'URL rewrite', - event:function(row,index,ev,key,that){ - bt.site.get_dir_rewrite({ id: row.id }, function (ret) { - if (!ret.status) { - var confirmObj = layer.confirm(lan.site.url_rewrite_alter, { icon: 3, closeBtn: 2 }, function () { - bt.site.get_dir_rewrite({ id: row.id, add: 1 }, function (ret) { - layer.close(confirmObj); - show_dir_rewrite(ret); + column: [ + {type: 'checkbox', width: 20, keepNumber: 1}, + {fid: 'domain', title: lan.site.domain, type: 'text'}, + {fid: 'port', title: lan.site.port, width: 70, type: 'text'}, + {fid: 'path', title: lan.site.subdirectories, width: 70, type: 'text'}, + { + title: 'Opt', width: 130, type: 'group', align: 'right', group: [{ + title: 'URL rewrite', + event: function (row, index, ev, key, that) { + bt.site.get_dir_rewrite({id: row.id}, function (ret) { + if (!ret.status) { + var confirmObj = layer.confirm(lan.site.url_rewrite_alter, { + icon: 3, + closeBtn: 2 + }, function () { + bt.site.get_dir_rewrite({id: row.id, add: 1}, function (ret) { + layer.close(confirmObj); + show_dir_rewrite(ret); + }); }); - }); - return; - } - show_dir_rewrite(ret); - function get_rewrite_file(name){ - var spath = '/www/server/panel/rewrite/' + (bt.get_cookie('serverType') == 'openlitespeed'?'apache':bt.get_cookie('serverType')) + '/' + name + '.conf'; - if(bt.get_cookie('serverType') == 'nginx'){ - if(name == 'default') spath = '/www/server/panel/vhost/rewrite/'+ web.name +'_'+row['path'] + '.conf'; - }else{ - if(name == 'default') spath = '/www/wwwroot/'+ web.name +'/'+row['path'] + '.htaccess'; + return; } - bt.files.get_file_body(spath, function(sdata){ - $('.dir_config').text(sdata.data); - }); - } - function show_dir_rewrite(ret){ - var load_form = bt.open({ - type: 1, - area: ['510px','530px'], - title: lan.site.config_url, - closeBtn: 2, - shift: 5, - skin: 'bt-w-con', - shadeClose: true, - content: "
                                      ", - success:function(){ - var _html = $(".webedit-dir-box"),arrs = []; - for (var i = 0; i < ret.rlist.length; i++){ - if(i == 0){ - arrs.push({ title: ret.rlist[i], value: 'default'}); - }else{ - arrs.push({ title: ret.rlist[i], value: ret.rlist[i] }); - } - } - var datas = [{ - name: 'dir_rewrite', type: 'select', width: '130px', items: arrs, callback: function (obj) { - get_rewrite_file(obj.val()); - } - }, - { items: [{ name: 'dir_config', type: 'textarea', value: ret.data, width: '470px', height: '260px' }] }, - { - items: [{ - name: 'btn_save', text: 'Save', type: 'button', callback: function (ldata) { - console.log(ret) - bt.files.set_file_body(ret.filename, ldata.dir_config, 'utf-8', function (sdata) { - if (sdata.status) load_form.close(); - bt.msg(sdata); - }) - } - }] - }] - var clicks = []; - for (var i = 0; i < datas.length; i++) { - var _form_data = bt.render_form_line(datas[i]); - _html.append(_form_data.html); - var _other = (bt.os == 'Linux' && i == 0) ? 'Rewrite rule converter:Apache to Nginx' : ''; - _html.find('.info-r').append(_other) - clicks = clicks.concat(_form_data.clicks); - } - _html.append(bt.render_help(['Please select your application.', 'If the site cannot be accessed after the rewrite rules set, please try to reset to default.','You are able to modify rewrite rules, just save it after modification.'])); - bt.render_clicks(clicks); - get_rewrite_file($('.dir_rewrite option:eq(0)').val()); + show_dir_rewrite(ret); + + function get_rewrite_file(name) { + var spath = '/www/server/panel/rewrite/' + (bt.get_cookie('serverType') == 'openlitespeed' ? 'apache' : bt.get_cookie('serverType')) + '/' + name + '.conf'; + if (bt.get_cookie('serverType') == 'nginx') { + if (name == 'default') spath = '/www/server/panel/vhost/rewrite/' + web.name + '_' + row['path'] + '.conf'; + } else { + if (name == 'default') spath = '/www/wwwroot/' + web.name + '/' + row['path'] + '.htaccess'; } - }); - } - }) - } - },{ - title:'Del', - event:function(row,index,ev,key,that){ - bt.confirm({title:'Are you sure to delete this【'+ row.path +'】 subdirectory binding?', msg: lan.site.s_bin_del }, function () { - bt.site.del_dirbind(row.id, function (res) { - if(res.status) that.$delete_table_row(index); - bt.msg(res); + bt.files.get_file_body(spath, function (sdata) { + $('.dir_config').text(sdata.data); + }); + } + + function show_dir_rewrite(ret) { + var load_form = bt.open({ + type: 1, + area: ['510px', '530px'], + title: lan.site.config_url, + closeBtn: 2, + shift: 5, + skin: 'bt-w-con', + shadeClose: true, + content: "
                                      ", + success: function () { + var _html = $(".webedit-dir-box"), arrs = []; + for (var i = 0; i < ret.rlist.length; i++) { + if (i == 0) { + arrs.push({title: ret.rlist[i], value: 'default'}); + } else { + arrs.push({title: ret.rlist[i], value: ret.rlist[i]}); + } + } + var datas = [{ + name: 'dir_rewrite', + type: 'select', + width: '130px', + items: arrs, + callback: function (obj) { + get_rewrite_file(obj.val()); + } + }, + { + items: [{ + name: 'dir_config', + type: 'textarea', + value: ret.data, + width: '470px', + height: '260px' + }] + }, + { + items: [{ + name: 'btn_save', + text: 'Save', + type: 'button', + callback: function (ldata) { + console.log(ret) + bt.files.set_file_body(ret.filename, ldata.dir_config, 'utf-8', function (sdata) { + if (sdata.status) load_form.close(); + bt.msg(sdata); + }) + } + }] + }] + var clicks = []; + for (var i = 0; i < datas.length; i++) { + var _form_data = bt.render_form_line(datas[i]); + _html.append(_form_data.html); + var _other = (bt.os == 'Linux' && i == 0) ? 'Rewrite rule converter:Apache to Nginx' : ''; + _html.find('.info-r').append(_other) + clicks = clicks.concat(_form_data.clicks); + } + _html.append(bt.render_help(['Please select your application.', 'If the site cannot be accessed after the rewrite rules set, please try to reset to default.', 'You are able to modify rewrite rules, just save it after modification.'])); + bt.render_clicks(clicks); + get_rewrite_file($('.dir_rewrite option:eq(0)').val()); + } + }); + } }) - }); - } - }] - }], - tootls:[{ // 批量操作 - type:'batch', - positon:['left','bottom'], - config:{ - title:' execute', - url:'/site?action=delete_dir_bind_multiple', - param:{id:web.id}, - paramId:'id', - paramName:'bind_ids', - theadName:'Domain', - confirmVerify:false //是否提示验证方式 + } + }, { + title: 'Del', + event: function (row, index, ev, key, that) { + bt.confirm({ + title: 'Are you sure to delete this【' + row.path + '】 subdirectory binding?', + msg: lan.site.s_bin_del + }, function () { + bt.site.del_dirbind(row.id, function (res) { + if (res.status) that.$delete_table_row(index); + bt.msg(res); + }) + }); + } + }] + }], + tootls: [{ // 批量操作 + type: 'batch', + positon: ['left', 'bottom'], + config: { + title: ' execute', + url: '/site?action=delete_dir_bind_multiple', + param: {id: web.id}, + paramId: 'id', + paramName: 'bind_ids', + theadName: 'Domain', + confirmVerify: false //是否提示验证方式 } }] }); }, - set_dirpath: function(web) { + set_dirpath: function (web) { var loading = bt.load(); - bt.site.get_site_path(web.id, function(path) { - bt.site.get_dir_userini(web.id, path, function(rdata) { + bt.site.get_site_path(web.id, function (path) { + bt.site.get_dir_userini(web.id, path, function (rdata) { loading.close(); var dirs = []; var is_n = false; for (var n = 0; n < rdata.runPath.dirs.length; n++) { - dirs.push({ title: rdata.runPath.dirs[n], value: rdata.runPath.dirs[n] }); + dirs.push({title: rdata.runPath.dirs[n], value: rdata.runPath.dirs[n]}); if (rdata.runPath.runPath === rdata.runPath.dirs[n]) is_n = true; } - if (!is_n) dirs.push({ title: rdata.runPath.runPath, value: rdata.runPath.runPath }); + if (!is_n) dirs.push({title: rdata.runPath.runPath, value: rdata.runPath.runPath}); var datas = [{ - title: '', - items: [{ - name: 'userini', - type: 'checkbox', - text: lan.site.anti_XSS_attack + '(open_basedir)', - value: rdata.userini, - callback: function(sdata) { - bt.site.set_dir_userini(path, web.id, function(ret) { - if (ret.status) site.reload(2) - layer.msg(ret.msg, { icon: ret.status ? 1 : 2 }); - }) - } - }, - { - name: 'logs', - type: 'checkbox', - text: lan.site.write_access_log, - value: rdata.logs, - callback: function(sdata) { - bt.site.set_logs_status(web.id, function(ret) { - if (ret.status) site.reload(2) - layer.msg(ret.msg, { icon: ret.status ? 1 : 2 }); - }) - } - } - ] + title: '', + items: [{ + name: 'userini', + type: 'checkbox', + text: lan.site.anti_XSS_attack + '(open_basedir)', + value: rdata.userini, + callback: function (sdata) { + bt.site.set_dir_userini(path, web.id, function (ret) { + if (ret.status) site.reload(2) + layer.msg(ret.msg, {icon: ret.status ? 1 : 2}); + }) + } }, + { + name: 'logs', + type: 'checkbox', + text: lan.site.write_access_log, + value: rdata.logs, + callback: function (sdata) { + bt.site.set_logs_status(web.id, function (ret) { + if (ret.status) site.reload(2) + layer.msg(ret.msg, {icon: ret.status ? 1 : 2}); + }) + } + } + ] + }, { title: '', items: [ - { name: 'path', title: lan.site.site_menu_2, width: '240px', value: path, add_class: 'ml5', event: { css: 'glyphicon-folder-open', callback: function(obj) { bt.select_path(obj); } } }, + { + name: 'path', + title: lan.site.site_menu_2, + width: '240px', + value: path, + add_class: 'ml5', + event: { + css: 'glyphicon-folder-open', callback: function (obj) { + bt.select_path(obj); + } + } + }, { name: 'btn_site_path', type: 'button', text: lan.site.save, - callback: function(pdata) { - bt.site.set_site_path_new(web.id, pdata.path, web.name, function(ret) { + callback: function (pdata) { + bt.site.set_site_path_new(web.id, pdata.path, web.name, function (ret) { if (ret.status) site.reload(2) - layer.msg(ret.msg, { icon: ret.status ? 1 : 2 }); + layer.msg(ret.msg, {icon: ret.status ? 1 : 2}); }) } } @@ -4179,15 +4703,23 @@ var site = { { title: '', items: [ - { title: lan.site.run_dir, width: '240px', value: rdata.runPath.runPath, name: 'dirName', type: 'select',add_class: 'ml5 mr20', items: dirs }, + { + title: lan.site.run_dir, + width: '240px', + value: rdata.runPath.runPath, + name: 'dirName', + type: 'select', + add_class: 'ml5 mr20', + items: dirs + }, { name: 'btn_run_path', type: 'button', text: lan.site.save, - callback: function(pdata) { - bt.site.set_site_runpath(web.id, pdata.dirName, function(ret) { + callback: function (pdata) { + bt.site.set_site_runpath(web.id, pdata.dirName, function (ret) { if (ret.status) site.reload(2) - layer.msg(ret.msg, { icon: ret.status ? 1 : 2 }); + layer.msg(ret.msg, {icon: ret.status ? 1 : 2}); }) } } @@ -4201,7 +4733,7 @@ var site = { _html.append($(_form_data.html).addClass('line mtb10')); clicks = clicks.concat(_form_data.clicks); } - _html.find('input[name="path"]').parent().css('padding-left','27px'); + _html.find('input[name="path"]').parent().css('padding-left', '27px'); _html.find('input[type="checkbox"]').parent().addClass('label-input-group ptb10'); _html.find('button[name="btn_run_path"]').addClass('ml45'); _html.find('button[name="btn_site_path"]').addClass('ml33'); @@ -4210,25 +4742,42 @@ var site = { $('#webedit-con').append(_html); bt.render_clicks(clicks); - $('#pathSafe').click(function() { + $('#pathSafe').click(function () { var val = $(this).prop('checked'); var _div = $('.user_pw') if (val) { var dpwds = [ - { title: lan.site.access_account, width: '250px', name: 'username_get', placeholder: lan.site.no_change_set_empty }, - { title: lan.site.pass_visit, width: '250px', type: 'password', name: 'password_get_1', placeholder: lan.site.no_change_set_empty }, - { title: lan.site.pass_again, width: '250px', type: 'password', name: 'password_get_2', placeholder: lan.site.no_change_set_empty }, + { + title: lan.site.access_account, + width: '250px', + name: 'username_get', + placeholder: lan.site.no_change_set_empty + }, + { + title: lan.site.pass_visit, + width: '250px', + type: 'password', + name: 'password_get_1', + placeholder: lan.site.no_change_set_empty + }, + { + title: lan.site.pass_again, + width: '250px', + type: 'password', + name: 'password_get_2', + placeholder: lan.site.no_change_set_empty + }, { name: 'btn_password_get', text: lan.site.save, type: 'button', - callback: function(rpwd) { + callback: function (rpwd) { if (rpwd.password_get_1 != rpwd.password_get_2) { - layer.msg(lan.bt.pass_err_re, { icon: 2 }); + layer.msg(lan.bt.pass_err_re, {icon: 2}); return; } - bt.site.set_site_pwd(web.id, rpwd.username_get, rpwd.password_get_1, function(ret) { - layer.msg(ret.msg, { icon: ret.status ? 1 : 2 }) + bt.site.set_site_pwd(web.id, rpwd.username_get, rpwd.password_get_1, function (ret) { + layer.msg(ret.msg, {icon: ret.status ? 1 : 2}) if (ret.status) site.reload(2) }) } @@ -4240,8 +4789,8 @@ var site = { bt.render_clicks(_from_pwd.clicks); } } else { - bt.site.close_site_pwd(web.id, function(rdata) { - layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); + bt.site.close_site_pwd(web.id, function (rdata) { + layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); _div.html(''); }) } @@ -4250,189 +4799,191 @@ var site = { }) }) }, - set_dirguard: function(web) { + set_dirguard: function (web) { $('#webedit-con').html('
                                      '); var tab = '
                                      \ - Limit accessDeny access\ -
                                      \ -
                                      \ - '; + Limit accessDeny access\ + \ +
                                      \ + '; $("#set_dirguard").html(tab); var dir_dirguard = bt_tools.table({ - el:'#dir_dirguard', - url:'/site?action=get_dir_auth', - param:{id:web.id}, - dataFilter:function(res){ - return {data:res[web.name]}; + el: '#dir_dirguard', + url: '/site?action=get_dir_auth', + param: {id: web.id}, + dataFilter: function (res) { + return {data: res[web.name]}; }, - column:[ - {type:'checkbox',width:20}, - {fid:'name',title:lan.site.name,type:'text'}, - {fid:'site_dir',title:'Path',type:'text'}, - {title:lan.site.operate,width:110,type:'group',align:'right',group:[{ - title:lan.site.edit, - event:function(row,index,ev,key,that){ - site.edit.template_Dir(web.id,false,row); - } - },{ - title:lan.site.del, - event:function(row,index,ev,key,that){ - bt.site.delete_dir_guard(web.id,row.name,function(res){ - if(res.status) that.$delete_table_row(index); - bt.msg(res); - }); - } + column: [ + {type: 'checkbox', width: 20}, + {fid: 'name', title: lan.site.name, type: 'text'}, + {fid: 'site_dir', title: 'Path', type: 'text'}, + { + title: lan.site.operate, width: 110, type: 'group', align: 'right', group: [{ + title: lan.site.edit, + event: function (row, index, ev, key, that) { + site.edit.template_Dir(web.id, false, row); + } + }, { + title: lan.site.del, + event: function (row, index, ev, key, that) { + bt.site.delete_dir_guard(web.id, row.name, function (res) { + if (res.status) that.$delete_table_row(index); + bt.msg(res); + }); + } + }], }], - }], - tootls:[{ // 按钮组 - type:'group', - positon:['left','top'], - list:[{title:'Add limit access',active:true, event:function(ev){ - site.edit.template_Dir(web.id,true); - }}] - },{ // 批量操作 + tootls: [{ // 按钮组 + type: 'group', + positon: ['left', 'top'], + list: [{ + title: 'Add limit access', active: true, event: function (ev) { + site.edit.template_Dir(web.id, true); + } + }] + }, { // 批量操作 type: 'batch', positon: ['left', 'bottom'], config: { - title: " delete", - url: '/site?action=delete_dir_auth', - param: function (row) { - console.log(row) - return { - id: web.id, - name: row.name - } - }, - load: true, - callback: function (that) { // 手动执行,data参数包含所有选中的站点 - bt.show_confirm('Delete limit access','Do you want to delete limit access ?',function(){ - that.start_batch({}, function (list) { - var html = ''; - for (var i = 0; i < list.length; i++) { - var item = list[i]; - html += '' + item.name + '
                                      ' + item.request.msg + '
                                      '; - } - dir_dirguard.$batch_success_table({ - title: 'Limit access', - th: 'Limit access name', - html: html - }); - dir_dirguard.$refresh_table_list(true); - }); - }); - } - } + title: " delete", + url: '/site?action=delete_dir_auth', + param: function (row) { + return { + id: web.id, + name: row.name + } + }, + load: true, + callback: function (that) { // 手动执行,data参数包含所有选中的站点 + bt.show_confirm('Delete limit access', 'Do you want to delete limit access ?', function () { + that.start_batch({}, function (list) { + var html = ''; + for (var i = 0; i < list.length; i++) { + var item = list[i]; + html += '' + item.name + '' + item.request.msg + ''; + } + dir_dirguard.$batch_success_table({ + title: 'Limit access', + th: 'Limit access name', + html: html + }); + dir_dirguard.$refresh_table_list(true); + }); + }); + } + } }] }); var php_dirguard = bt_tools.table({ - el: '#php_dirguard', - url: '/config?action=get_file_deny', - param: { - website: web.name - }, - dataFilter: function (res) { - return { - data: res - }; - }, - column: [ - {type:'checkbox',width:20}, - { - fid: 'name', - title: lan.site.name, - type: 'text' + el: '#php_dirguard', + url: '/config?action=get_file_deny', + param: { + website: web.name }, - { - fid: 'dir', - title: 'Path', - type: 'text', - template: function (row) { - return '' + row.dir + ''; - } - }, - { - fid: 'suffix', - title: 'Suffix', - template: function (row) { - return '' + row.suffix + ''; - } - }, - { - title: lan.site.operate, - width: 110, - type: 'group', - align: 'right', - group: [{ - title: lan.site.edit, - event: function (row, index, ev, key, that) { - site.edit.template_php(web.name, row); - } - }, { - title: lan.site.del, - event: function (row, index, ev, key, that) { - site.edit.del_php_deny(web.name, row.name, function (res) { - if (res.status) that.$delete_table_row(index); - bt.msg(res); - }); - } - }], - } - ], - tootls: [{ // 按钮组 - type: 'group', - positon: ['left', 'top'], - list: [{ - title: 'Add deny access', - active: true, - event: function (ev) { - site.edit.template_php(web.name); - } - }] - }, { // 批量操作 - type: 'batch', - positon: ['left', 'bottom'], - config: { - title: " delete", - url: '/site?action=del_file_deny', - param: function (row) { + dataFilter: function (res) { return { - website: web.name, - deny_name: row.name - } - }, - load: true, - callback: function (that) { // 手动执行,data参数包含所有选中的站点 - bt.show_confirm('Delete deny access', 'Do you want to delete deny access?', function () { - that.start_batch({}, function (list) { - var html = ''; - for (var i = 0; i < list.length; i++) { - var item = list[i]; - html += '' + item.name + '
                                      ' + item.request.msg + '
                                      '; + data: res + }; + }, + column: [ + {type: 'checkbox', width: 20}, + { + fid: 'name', + title: lan.site.name, + type: 'text' + }, + { + fid: 'dir', + title: 'Path', + type: 'text', + template: function (row) { + return '' + row.dir + ''; } - php_dirguard.$batch_success_table({ - title: 'Deny access', - th: 'Deny access name', - html: html - }); - php_dirguard.$refresh_table_list(true); - }); - }); - } - } - }] + }, + { + fid: 'suffix', + title: 'Suffix', + template: function (row) { + return '' + row.suffix + ''; + } + }, + { + title: lan.site.operate, + width: 110, + type: 'group', + align: 'right', + group: [{ + title: lan.site.edit, + event: function (row, index, ev, key, that) { + site.edit.template_php(web.name, row); + } + }, { + title: lan.site.del, + event: function (row, index, ev, key, that) { + site.edit.del_php_deny(web.name, row.name, function (res) { + if (res.status) that.$delete_table_row(index); + bt.msg(res); + }); + } + }], + } + ], + tootls: [{ // 按钮组 + type: 'group', + positon: ['left', 'top'], + list: [{ + title: 'Add deny access', + active: true, + event: function (ev) { + site.edit.template_php(web.name); + } + }] + }, { // 批量操作 + type: 'batch', + positon: ['left', 'bottom'], + config: { + title: " delete", + url: '/site?action=del_file_deny', + param: function (row) { + return { + website: web.name, + deny_name: row.name + } + }, + load: true, + callback: function (that) { // 手动执行,data参数包含所有选中的站点 + bt.show_confirm('Delete deny access', 'Do you want to delete deny access?', function () { + that.start_batch({}, function (list) { + var html = ''; + for (var i = 0; i < list.length; i++) { + var item = list[i]; + html += '' + item.name + '
                                      ' + item.request.msg + '
                                      '; + } + php_dirguard.$batch_success_table({ + title: 'Deny access', + th: 'Deny access name', + html: html + }); + php_dirguard.$refresh_table_list(true); + }); + }); + } + } + }] }); - $('#dir_dirguard>.divtable,#php_dirguard>.divtable').css('max-height','340px'); + $('#dir_dirguard>.divtable,#php_dirguard>.divtable').css('max-height', '340px'); $('#dir_dirguard').append("
                                        \ -
                                      • After the path is protected, you need to enter the account password to access it.
                                      • \ -
                                      • For example, if I set the limit path /test/ , then I need to enter the account password to access http://aaa.com/test/
                                      • \ -
                                      "); +
                                    • After setting, you need to enter the password to access it.
                                    • \ +
                                    • For example, if I set the limit path /test/ , then I need to enter the account password to access http://aaa.com/test/
                                    • \ + "); $('#php_dirguard').append("
                                        \ -
                                      • Suffix: Indicates the suffix that is not allowed to access, if there are more than one, separate with'|'.
                                      • \ -
                                      • Path: Quote rules in this directory. e.g: /a/
                                      • \ -
                                      • For Example, if you want to deny http://test.com/a/index.php
                                      • \ -
                                      • Please fill in [ /a/ ]
                                      • \ -
                                      "); - $("#set_dirguard").on('click', '.tab-nav span',function () { +
                                    • Suffix: Indicates the suffix that is not allowed to access, if there are more than one, separate with'|'.
                                    • \ +
                                    • Path: Quote rules in this directory. e.g: /a/
                                    • \ +
                                    • For Example, if you want to deny http://test.com/a/index.php
                                    • \ +
                                    • Please fill in [ /a/ ]
                                    • \ + "); + $("#set_dirguard").on('click', '.tab-nav span', function () { var index = $(this).index(); $(this).addClass('on').siblings().removeClass('on'); if (index == 0) { @@ -4444,26 +4995,26 @@ var site = { } }); }, - ols_cache: function(web) { - bt.send('get_ols_static_cache', 'config/get_ols_static_cache', { id: web.id }, function(rdata) { + ols_cache: function (web) { + bt.send('get_ols_static_cache', 'config/get_ols_static_cache', {id: web.id}, function (rdata) { var clicks = [], newkey = [], newval = [], checked = false; - Object.keys(rdata).forEach(function(key){ - //for (let key in rdata) { + Object.keys(rdata).forEach(function (key) { + //for (let key in rdata) { newkey.push(key); newval.push(rdata[key]); }); - var datas = [{ title: newkey[0], name: newkey[0], width: '30%', value: newval[0] }, - { title: newkey[1], name: newkey[1], width: '30%', value: newval[1] }, - { title: newkey[2], name: newkey[2], width: '30%', value: newval[2] }, - { title: newkey[3], name: newkey[3], width: '30%', value: newval[3] }, + var datas = [{title: newkey[0], name: newkey[0], width: '30%', value: newval[0]}, + {title: newkey[1], name: newkey[1], width: '30%', value: newval[1]}, + {title: newkey[2], name: newkey[2], width: '30%', value: newval[2]}, + {title: newkey[3], name: newkey[3], width: '30%', value: newval[3]}, { name: 'static_save', text: lan.site.save, type: 'button', - callback: function(ldata) { + callback: function (ldata) { var cdata = {}, loadT = bt.load(); Object.assign(cdata, ldata); @@ -4471,27 +5022,30 @@ var site = { delete cdata.maxage; delete cdata.exclude_file; delete cdata.private_save; - bt.send('set_ols_static_cache', 'config/set_ols_static_cache', { values: JSON.stringify(cdata), id: web.id }, function(res) { + bt.send('set_ols_static_cache', 'config/set_ols_static_cache', { + values: JSON.stringify(cdata), + id: web.id + }, function (res) { loadT.close(); bt.msg(res) }); } }, - { title: 'test', name: 'test', width: '30%', value: '11' }, - { title: 'maxage', name: 'maxage', width: '30%', value: '43200' }, - { title: 'exclude file', name: 'exclude_file', width: '35%', value: 'fdas.php', }, + {title: 'test', name: 'test', width: '30%', value: '11'}, + {title: 'maxage', name: 'maxage', width: '30%', value: '43200'}, + {title: 'exclude file', name: 'exclude_file', width: '35%', value: 'fdas.php',}, { name: 'private_save', text: lan.site.save, type: 'button', - callback: function(ldata) { + callback: function (ldata) { var edata = {}, loadT = bt.load(); if (checked) { edata.id = web.id; edata.max_age = parseInt($("input[name='maxage']").val()); edata.exclude_file = $("textarea[name='exclude_file']").val(); - bt.send('set_ols_private_cache', 'config/set_ols_private_cache', edata, function(res) { + bt.send('set_ols_private_cache', 'config/set_ols_private_cache', edata, function (res) { loadT.close(); bt.msg(res) }); @@ -4504,7 +5058,8 @@ var site = { var _form_data = bt.render_form_line(datas[i]); _html.append(_form_data.html); clicks = clicks.concat(_form_data.clicks); - }; + } + ; $('#webedit-con').append(_html); $("input[name='exclude_file']").parent().removeAttr('class').html(''); $("input[name='test']").parent().parent().html('
                                      private cache
                                      '); @@ -4513,10 +5068,10 @@ var site = { $("button[name='static_save']").parent().append(bt.render_help(['The default static file cache time is 604800 seconds', 'If you want to shut down, please change it to 0 seconds'])); $(".ols").append(bt.render_help(['Private cache only supports page caching for PHP and cache time is 120 seconds by default', 'Exclude files only support files with PHP as the suffix'])); private.hide(); - bt.send('get_ols_private_cache_status', 'config/get_ols_private_cache_status', { id: web.id }, function(kdata) { + bt.send('get_ols_private_cache_status', 'config/get_ols_private_cache_status', {id: web.id}, function (kdata) { checked = kdata; if (kdata) { - bt.send('get_ols_private_cache', 'config/get_ols_private_cache', { id: web.id }, function(fdata) { + bt.send('get_ols_private_cache', 'config/get_ols_private_cache', {id: web.id}, function (fdata) { $("input[name='maxage']").val(fdata.maxage); var ss = fdata.exclude_file.join(" "); $("textarea[name='exclude_file']").html(ss); @@ -4525,15 +5080,15 @@ var site = { }); } }); - $('#ols').on('click', function() { + $('#ols').on('click', function () { var loadT = bt.load(); - bt.send('switch_ols_private_cache', 'config/switch_ols_private_cache', { id: web.id }, function(res) { + bt.send('switch_ols_private_cache', 'config/switch_ols_private_cache', {id: web.id}, function (res) { loadT.close(); private.toggle(); checked = private.is(':hidden') ? false : true; bt.msg(res); if (checked) { - bt.send('get_ols_private_cache', 'config/get_ols_private_cache', { id: web.id }, function(fdata) { + bt.send('get_ols_private_cache', 'config/get_ols_private_cache', {id: web.id}, function (fdata) { private.show(); $("input[name='maxage']").val(fdata.maxage); $("textarea[name='exclude_file']").html(fdata.exclude_file.join(" ")); @@ -4547,59 +5102,79 @@ var site = { $('.ols .btn-success').css("margin-left", "125px"); }) }, - limit_network: function(web) { - bt.site.get_limitnet(web.id, function(rdata) { + limit_network: function (web) { + bt.site.get_limitnet(web.id, function (rdata) { var limits = [ - { title: lan.site.bbs_or_blog, value: 1, items: { perserver: 300, perip: 25, limit_rate: 512 } }, - { title: lan.site.photo_station, value: 2, items: { perserver: 200, perip: 10, limit_rate: 1024 } }, - { title: lan.site.download_station, value: 3, items: { perserver: 50, perip: 3, limit_rate: 2048 } }, - { title: lan.site.mall, value: 4, items: { perserver: 500, perip: 10, limit_rate: 2048 } }, - { title: lan.site.portal_site, value: 5, items: { perserver: 400, perip: 15, limit_rate: 1024 } }, - { title: lan.site.enterprise, value: 6, items: { perserver: 60, perip: 10, limit_rate: 512 } }, - { title: lan.site.video, value: 7, items: { perserver: 150, perip: 4, limit_rate: 1024 } } + {title: lan.site.bbs_or_blog, value: 1, items: {perserver: 300, perip: 25, limit_rate: 512}}, + {title: lan.site.photo_station, value: 2, items: {perserver: 200, perip: 10, limit_rate: 1024}}, + {title: lan.site.download_station, value: 3, items: {perserver: 50, perip: 3, limit_rate: 2048}}, + {title: lan.site.mall, value: 4, items: {perserver: 500, perip: 10, limit_rate: 2048}}, + {title: lan.site.portal_site, value: 5, items: {perserver: 400, perip: 15, limit_rate: 1024}}, + {title: lan.site.enterprise, value: 6, items: {perserver: 60, perip: 10, limit_rate: 512}}, + {title: lan.site.video, value: 7, items: {perserver: 150, perip: 4, limit_rate: 1024}} ] var datas = [{ - items: [{ - name: 'status', - type: 'checkbox', - value: rdata.perserver != 0 ? true : false, - text: lan.site.limit_net_8, - callback: function(ldata) { - if (ldata.status) { - bt.site.set_limitnet(web.id, ldata.perserver, ldata.perip, ldata.limit_rate, function(ret) { - layer.msg(ret.msg, { icon: ret.status ? 1 : 2 }); - if (ret.status) site.reload(3) - }) - } else { - bt.site.close_limitnet(web.id, function(ret) { - layer.msg(ret.msg, { icon: ret.status ? 1 : 2 }); - if (ret.status) site.reload(3) - }) - } + items: [{ + name: 'status', + type: 'checkbox', + value: rdata.perserver != 0 ? true : false, + text: lan.site.limit_net_8, + callback: function (ldata) { + if (ldata.status) { + bt.site.set_limitnet(web.id, ldata.perserver, ldata.perip, ldata.limit_rate, function (ret) { + layer.msg(ret.msg, {icon: ret.status ? 1 : 2}); + if (ret.status) site.reload(3) + }) + } else { + bt.site.close_limitnet(web.id, function (ret) { + layer.msg(ret.msg, {icon: ret.status ? 1 : 2}); + if (ret.status) site.reload(3) + }) } - }] - }, + } + }] + }, { title: lan.site.limit_net_9 + ' ', width: '160px', name: 'limit', type: 'select', items: limits, - callback: function(obj) { - var data = limits.filter(function(p) { return p.value === parseInt(obj.val()); })[0] + callback: function (obj) { + var data = limits.filter(function (p) { + return p.value === parseInt(obj.val()); + })[0] for (var key in data.items) $('input[name="' + key + '"]').val(data.items[key]); } }, - { title: lan.site.limit_net_10 + ' ', type: 'number', width: '200px', value: rdata.perserver, name: 'perserver' }, - { title: lan.site.limit_net_12 + ' ', type: 'number', width: '200px', value: rdata.perip, name: 'perip' }, - { title: lan.site.limit_net_14 + ' ', type: 'number', width: '200px', value: rdata.limit_rate, name: 'limit_rate' }, + { + title: lan.site.limit_net_10 + ' ', + type: 'number', + width: '200px', + value: rdata.perserver, + name: 'perserver' + }, + { + title: lan.site.limit_net_12 + ' ', + type: 'number', + width: '200px', + value: rdata.perip, + name: 'perip' + }, + { + title: lan.site.limit_net_14 + ' ', + type: 'number', + width: '200px', + value: rdata.limit_rate, + name: 'limit_rate' + }, { name: 'btn_limit_get', text: lan.site.save, type: 'button', - callback: function(ldata) { - bt.site.set_limitnet(web.id, ldata.perserver, ldata.perip, ldata.limit_rate, function(ret) { - layer.msg(ret.msg, { icon: ret.status ? 1 : 2 }); + callback: function (ldata) { + bt.site.set_limitnet(web.id, ldata.perserver, ldata.perip, ldata.limit_rate, function (ret) { + layer.msg(ret.msg, {icon: ret.status ? 1 : 2}); if (ret.status) site.reload(3) }) } @@ -4615,74 +5190,82 @@ var site = { _html.find('input[type="checkbox"]').parent().addClass('label-input-group ptb10'); _html.append(bt.render_help([lan.site.limit_net_11, lan.site.limit_net_13, lan.site.limit_net_15])); $('#webedit-con').append(_html); - $('.newnanme .tname').css('width','138px'); + $('.newnanme .tname').css('width', '138px'); bt.render_clicks(clicks); if (rdata.perserver == 0) $("select[name='limit']").trigger("change"); $('#status,.btn_limit_get').css("margin-left", "138px"); }) }, - get_rewrite_list: function(web) { + get_rewrite_list: function (web) { var filename = '/www/server/panel/vhost/rewrite/' + web.name + '.conf'; - bt.site.get_rewrite_list(web.name, function(rdata) { + bt.site.get_rewrite_list(web.name, function (rdata) { var arrs = [], webserver = bt.get_cookie('serverType'); if (webserver == 'apache' || webserver == 'openlitespeed') filename = rdata.sitePath + '/.htaccess'; if (webserver == 'openlitespeed') webserver = 'apache'; - for (var i = 0; i < rdata.rewrite.length; i++) arrs.push({ title: rdata.rewrite[i], value: rdata.rewrite[i] }); + for (var i = 0; i < rdata.rewrite.length; i++) arrs.push({ + title: rdata.rewrite[i], + value: rdata.rewrite[i] + }); var datas = [{ - name: 'rewrite', - type: 'select', - width: '130px', - items: arrs, - callback: function(obj) { - if (bt.os == 'Linux') { - var spath = filename; - if (obj.val() != lan.site.rewritename) spath = '/www/server/panel/rewrite/' + (webserver == 'openlitespeed'?'apache':webserver) + '/' + obj.val() + '.conf'; - bt.files.get_file_body(spath, function(ret) { - if (ret.status == false) { - layer.msg(ret.msg,{icon: 2}); - return false; - } - aceEditor.ACE.setValue(ret.data); - aceEditor.ACE.moveCursorTo(0, 0); - aceEditor.path = spath; - }) - } + name: 'rewrite', + type: 'select', + width: '130px', + items: arrs, + callback: function (obj) { + if (bt.os == 'Linux') { + var spath = filename; + if (obj.val() != lan.site.rewritename) spath = '/www/server/panel/rewrite/' + (webserver == 'openlitespeed' ? 'apache' : webserver) + '/' + obj.val() + '.conf'; + bt.files.get_file_body(spath, function (ret) { + if (ret.status == false) { + layer.msg(ret.msg, {icon: 2}); + return false; + } + aceEditor.ACE.setValue(ret.data); + aceEditor.ACE.moveCursorTo(0, 0); + aceEditor.path = spath; + }) } - }, - { items: [{ name: 'config', type: 'div', value: rdata.data, widht: '340px', height: '200px' }] }, + } + }, + {items: [{name: 'config', type: 'div', value: rdata.data, widht: '340px', height: '200px'}]}, { items: [{ - name: 'btn_save', - text: lan.site.save, - type: 'button', - callback: function(ldata) { - // bt.files.set_file_body(filename, editor.getValue(), 'utf-8', function(ret) { - // if (ret.status) site.reload(4) - // bt.msg(ret); - // }) - aceEditor.path = filename; - bt.saveEditor(aceEditor); - } - }, + name: 'btn_save', + text: lan.site.save, + type: 'button', + callback: function (ldata) { + // bt.files.set_file_body(filename, editor.getValue(), 'utf-8', function(ret) { + // if (ret.status) site.reload(4) + // bt.msg(ret); + // }) + aceEditor.path = filename; + bt.saveEditor(aceEditor); + } + }, { name: 'btn_save_to', text: lan.site.save_as_template, type: 'button', - callback: function(ldata) { + callback: function (ldata) { var temps = { title: lan.site.save_rewrite_temp, area: '330px', list: [ - { title: lan.site.template_name, placeholder: lan.site.template_name, width: '160px', name: 'tempname' } + { + title: lan.site.template_name, + placeholder: lan.site.template_name, + width: '160px', + name: 'tempname' + } ], btns: [ - { title: lan.site.turn_off, name: 'close' }, + {title: lan.site.turn_off, name: 'close'}, { title: lan.site.submit, name: 'submit', css: 'btn-success', - callback: function(rdata, load, callback) { - bt.site.set_rewrite_tel(rdata.tempname, aceEditor.ACE.getValue(), function(rRet) { + callback: function (rdata, load, callback) { + bt.site.set_rewrite_tel(rdata.tempname, aceEditor.ACE.getValue(), function (rRet) { if (rRet.status) { load.close(); site.reload(4) @@ -4708,7 +5291,7 @@ var site = { _html.find('.info-r').append(_other) clicks = clicks.concat(_form_data.clicks); } - _html.append(bt.render_help([lan.site.rewrite_tips_1,lan.site.rewrite_tips_2, lan.site.edit_rewrite])); + _html.append(bt.render_help([lan.site.rewrite_tips_1, lan.site.rewrite_tips_2, lan.site.edit_rewrite])); $('#webedit-con').append(_html); bt.render_clicks(clicks); @@ -4724,26 +5307,31 @@ var site = { // setTimeout(function() { // editor.refresh(); // }, 250); - $('div.config').attr('id', 'config_rewrite').css({'height':'360px','width':'540px'}) - var aceEditor = bt.aceEditor({el:'config_rewrite',content:rdata.data}); + $('div.config').attr('id', 'config_rewrite').css({'height': '360px', 'width': '540px'}) + var aceEditor = bt.aceEditor({el: 'config_rewrite', content: rdata.data}); $('select.rewrite').trigger('change'); }) }, - set_default_index: function(web) { - bt.site.get_index(web.id, function(rdata) { + set_default_index: function (web) { + bt.site.get_index(web.id, function (rdata) { rdata = rdata.replace(new RegExp(/(,)/g), "\n"); var data = { items: [ - { name: 'Dindex', height: '230px', width: '50%', type: 'textarea', value: rdata }, + {name: 'Dindex', height: '230px', width: '50%', type: 'textarea', value: rdata}, { name: 'btn_submit', text: lan.site.add, type: 'button', - callback: function(ddata) { + callback: function (ddata) { var Dindex = ddata.Dindex.replace(new RegExp(/(\n)/g), ","); - bt.site.set_index(web.id, Dindex, function(ret) { - if (ret.status) site.reload(5) + bt.site.set_index(web.id, Dindex, function (ret) { + if (!ret.status){ + bt.msg(ret); + return; + } + + site.reload(5) }) } } @@ -4757,27 +5345,30 @@ var site = { bt.render_clicks(_form_data.clicks); }) }, - set_config: function(web) { + set_config: function (web) { var con = '

                                      Tips:Ctrl+F Search keywords,Ctrl+S Save,Ctrl+H Search and replace

                                      \ - \ -
                                        \ -
                                      • This is primary configuration file of the site.
                                      • \ -
                                      • Do not modify it at will if you do not know configuration rules.
                                      • \ -
                                      '; + \ +
                                        \ +
                                      • This is primary configuration file of the site.
                                      • \ +
                                      • Do not modify it at will if you do not know configuration rules.
                                      • \ +
                                      '; $("#webedit-con").html(con); var webserve = bt.get_cookie('serverType'), - config = bt.aceEditor({ el: 'siteConfigBody', path: '/www/server/panel/vhost/' + (webserve == 'openlitespeed' ? (webserve + '/detail') : webserve) + '/' + web.name + '.conf' }); - $("#OnlineEditFileBtn").click(function(e) { + config = bt.aceEditor({ + el: 'siteConfigBody', + path: '/www/server/panel/vhost/' + (webserve == 'openlitespeed' ? (webserve + '/detail') : webserve) + '/' + web.name + '.conf' + }); + $("#OnlineEditFileBtn").click(function (e) { bt.saveEditor(config); }); }, - set_php_version: function(web) { - bt.site.get_site_phpversion(web.name, function(sdata) { + set_php_version: function (web) { + bt.site.get_site_phpversion(web.name, function (sdata) { if (sdata.status === false) { bt.msg(sdata); return; } - bt.site.get_all_phpversion(function(vdata) { + bt.site.get_all_phpversion(function (vdata) { var versions = []; for (var j = vdata.length - 1; j >= 0; j--) { var o = vdata[j]; @@ -4856,31 +5447,31 @@ var site = { name: 'versions', value: sdata.phpversion, type: 'select', - items: versions , - ps:'' + items: versions, + ps: '' }, { text: 'Switch', name: 'btn_change_phpversion', type: 'button', - callback: function(pdata) { + callback: function (pdata) { var other = $('.other-version').val(); - if(pdata.versions == 'other' && other == ''){ + if (pdata.versions == 'other' && other == '') { layer.msg('When customizing the PHP version, the PHP connection configuration cannot be empty'); $('.other-version').focus(); return; } - bt.site.set_phpversion(web.name, pdata.versions, other, function(ret) { + bt.site.set_phpversion(web.name, pdata.versions, other, function (ret) { if (ret.status) { var versions = $('[name="versions"]').val(); versions = versions.slice(0, versions.length - 1) + '.' + versions.slice(-1); if (versions == '0.0') versions = 'Static'; - site.php_table_view(); - site.reload(); - setTimeout(function() { + site_table.$refresh_table_list(true); + site.reload() + setTimeout(function () { bt.msg(ret); }, 1000); - }else{ + } else { bt.msg(ret); } }) @@ -4890,49 +5481,209 @@ var site = { } var _form_data = bt.render_form_line(data); var _html = $(_form_data.html); - _html.append(bt.render_help(['Select the version according to your program requirements', 'Try not to use PHP5.2 unless you have to, as this can reduce your server security', 'PHP7 does not support the MySQL extension. The default installation is mysqli and mysql-pdo',"[Customize] You can customize the PHP connection information by selecting the available PHP connection configuration","[Customize] Currently only support NGINX","Support TCP or UNIX configuration. Example: 192.168.1.25:9001 or unix:/tmp/php8.sock"])); + _html.append(bt.render_help(['Select the version according to your program requirements', 'Try not to use PHP5.2 unless you have to, as this can reduce your server security', 'PHP7 does not support the MySQL extension. The default installation is mysqli and mysql-pdo', "[Customize] You can customize the PHP connection information by selecting the available PHP connection configuration", "[Customize] Currently only support NGINX", "Support TCP or UNIX configuration. Example: 192.168.1.25:9001 or unix:/tmp/php8.sock"])); $('#webedit-con').append(_html); bt.render_clicks(_form_data.clicks); - if(sdata.phpversion != 'other'){ + if (sdata.phpversion != 'other') { $('#webedit-con').append('
                                      ' + lan.site.session_off + '
                                      ' + - bt.render_help(['When enabled, session files will be stored in a separate folder, not in a common storage location with other sites', 'Do not enable this option if you are saving sessions to caches such as memcache/redis in your PHP configuration'])); + bt.render_help(['When enabled, session files will be stored in a separate folder, not in a common storage location with other sites', 'Do not enable this option if you are saving sessions to caches such as memcache/redis in your PHP configuration'])); } - if(sdata.phpversion != 'other'){ + if (sdata.phpversion != 'other') { $('.other-version').hide(); } - setTimeout(function(){ - $('select[name="versions"]').change(function(){ + setTimeout(function () { + $('select[name="versions"]').change(function () { var phpversion = $(this).val(); // console.log(phpversion); - if(phpversion == 'other'){ + if (phpversion == 'other') { $('.other-version').show(); - }else{ + } else { $('.other-version').hide(); } }); - },500); + }, 500); function get_session_status() { var loading = bt.load('Please wait while getting session status'); - bt.send('get_php_session_path', 'config/get_php_session_path', { id: web.id }, function(tdata) { + bt.send('get_php_session_path', 'config/get_php_session_path', {id: web.id}, function (tdata) { loading.close(); $('#session_switch').prop("checked", tdata); }) }; get_session_status() - $('#session_switch').click(function() { + $('#session_switch').click(function () { var val = $(this).prop('checked'); - bt.send('set_php_session_path', 'config/set_php_session_path', { id: web.id, act: val ? 1 : 0 }, function(rdata) { + bt.send('set_php_session_path', 'config/set_php_session_path', { + id: web.id, + act: val ? 1 : 0 + }, function (rdata) { bt.msg(rdata) }) - setTimeout(function() { + setTimeout(function () { get_session_status(); }, 500); }) }) }) }, - templet_301: function(sitename, id, types, obj) { + set_wp_config:function(web){ + var loadup = bt.load('Getting Wordpress information, please wait...'); + bt.send('is_update','site/is_update',{s_id:web.id},function(rdata){ + loadup.close(); + var loadin = bt.load('Getting wordpress account information, please wait...'); + bt.send('get_wp_username','site/get_wp_username',{s_id:web.id},function(wlist){ + loadin.close(); + var robj = $('#webedit-con'); + + if (wlist.status === false) { + wlist.time = 0; + wlist.closeBtn = 2; + bt.msg(wlist); + var data = { + items: [ + { + title: 'Database name', + name: 'database', + value: '', + type: 'input', + width: '250px', + placeholder: 'Please enter the database name' + }, + { + text: 'Set', + name: 'btn_change_database', + type: 'button', + callback: function (pdata) { + if (pdata.database == '') { + return layer.msg('The database name cannot be empty', { icon: 2 }); + } + var param = { + site_id: web.id, + db_name: pdata.database + } + var load = bt.load('Setting Database name, please wait...'); + bt.send('reset_wp_db', 'site/reset_wp_db', param, function (res) { + load.close(); + bt.msg(res); + if (res.status) { + setTimeout(function () { + robj.html('') + site.edit.set_wp_config(web) + }, 1500) + } + }); + } + } + ] + } + var _form_data = bt.render_form_line(data); + var _html = $(_form_data.html); + _html.append(bt.render_help([ + 'Please enter the database name for this wordpress website' + ])); + robj.append(_html); + bt.render_clicks(_form_data.clicks); + return + } + + var _html = $('
                                      '), + user_array = [], + clicks = []; + + + $.each(wlist.msg,function(index,item){ + user_array.push({title:item,value:item}) + }) + var datas = [ + { + title: 'WP Version', + items:[{ + name:'wp_version', + type:'html', + html:(rdata['msg']['update']?'The current version is: '+rdata['msg']['local_v']+'':'The latest version') + }] + },{ + title:'Cache', + items:[ + { + type:'checkbox', + name:'cache_switch', + text:' Open cache', + value:web.cache_status, + callback:function(sdata){ + var loads = bt.load((sdata.cache_switch?'Turning on':'Turining off')+' [ '+web.name+' ] cache, please wait...'); + bt.send('set_fastcgi_cache','site/set_fastcgi_cache',{version:web.php_version,sitename:web.name,act:(sdata.cache_switch?'enable':'disable')},function(res){ + loads.close(); + bt.msg(res) + if(res.status){ + site.php_table_view(); + web.cache_status = sdata.cache_switch + } + }) + } + },{ + name: 'remove_cache', + text: 'Purge all cache', + type: 'button', + callback:function(sdata){ + var loadC = bt.load('Clearing all caches, please wait...'); + bt.send('purge_all_cache','site/purge_all_cache',{s_id:web.id},function(res){ + loadC.close(); + bt.msg(res) + }) + } + } + ] + },{ + title:'Reset password', + items:[{ + name:'user', + type:'select', + items:user_array, + width:'200px' + },{ + title:'', + name:'new_pass', + placeholder:'Please enter a new password', + width:'200px' + },{ + name: 'submit_pw', + text: 'Save password', + type: 'button', + callback:function(sdata){ + var loads = bt.load('Resetting password, please wait...') + bt.send('reset_wp_password','site/reset_wp_password',{s_id:web.id,user:sdata.user,new_pass:sdata.new_pass},function(res){ + loads.close(); + bt.msg(res) + }) + } + }] + } + ] + for (var i = 0; i < datas.length; i++) { + var _form_data = bt.render_form_line(datas[i]); + _html.append(_form_data.html); + clicks = clicks.concat(_form_data.clicks); + } + _html.find('input[type="checkbox"]').parent().addClass('label-input-group'); + _html.find('button[name="submit_pw"]').css('margin','15px 0') + robj.append(_html); + bt.render_clicks(clicks); + + + //wp版本更新 + $('.update_wp_version').click(function(){ + var load_wp= bt.load('Updating Wordpress version, please wait...'); + bt.send('update_wp','site/update_wp',{s_id:web.id,version:rdata['msg']['online_v']},function(res){ + load_wp.close(); + bt.msg(res) + if(res.status) $('.bt-w-menu.site-menu p.bgw').click() + }) + }) + }) + }) + }, + templet_301: function (sitename, id, types, obj) { if (types) { obj = { redirectname: (new Date()).valueOf(), @@ -4953,7 +5704,7 @@ var site = { lan.site.redirect_tips5, lan.site.redirect_tips6 ]; - bt.site.get_domains(id, function(rdata) { + bt.site.get_domains(id, function (rdata) { var domain_html = '' for (var i = 0; i < rdata.length; i++) { domain_html += ''; @@ -4984,7 +5735,7 @@ var site = { "
                                      " + "" + lan.site.redirect_type + "" + "
                                      " + - "" + + "" + "" + lan.site.redirect_mode + "" + "
                                      " + "
                                      " + @@ -4995,7 +5746,7 @@ var site = { "" + "" + lan.site.target_url + "" + "
                                      " + - ""+ + "" + "
                                      " + "" + "
                                      " + @@ -5010,7 +5761,7 @@ var site = { "
                                      " + "" }); - setTimeout(function() { + setTimeout(function () { $('.selectpicker').selectpicker({ 'noneSelectedText': lan.site.choose_domain, 'selectAllText': lan.site.choose_all, @@ -5018,7 +5769,7 @@ var site = { }); $('.selectpicker').selectpicker('val', obj.redirectdomain); $('#form_redirect').parent().css('overflow', 'inherit'); - $('[name="domainorpath"]').change(function() { + $('[name="domainorpath"]').change(function () { if ($(this).val() == 'path') { $('.redirectpath').show(); $('.redirectdomain').hide(); @@ -5029,10 +5780,10 @@ var site = { $('[name="redirectpath"]').val('') } }); - $('.btn-colse-prosy').click(function() { + $('.btn-colse-prosy').click(function () { form_redirect.close(); }); - $('.btn-submit-redirect').click(function() { + $('.btn-submit-redirect').click(function () { var type = $('[name="type"]').prop('checked') ? 1 : 0; var holdpath = $('[name="holdpath"]').prop('checked') ? 1 : 0; var redirectname = $('[name="redirectname"]').val(); @@ -5052,7 +5803,7 @@ var site = { redirectpath: redirectpath, redirectdomain: redirectdomain, tourl: tourl - }, function(rdata) { + }, function (rdata) { if (rdata.status) { form_redirect.close(); site.reload(11); @@ -5070,7 +5821,7 @@ var site = { redirectpath: redirectpath, redirectdomain: redirectdomain, tourl: tourl - }, function(rdata) { + }, function (rdata) { if (rdata.status) { form_redirect.close(); site.reload(11); @@ -5081,13 +5832,12 @@ var site = { }); }, 100); }); - }, - template_Dir: function(id, type, obj) { + template_Dir: function (id, type, obj) { if (type) { - obj = { "name": "", "sitedir": "", "username": "", "password": "" }; + obj = {"name": "", "sitedir": "", "username": "", "password": ""}; } else { - obj = { "name": obj.name, "sitedir": obj.site_dir, "username": "", "password": "" }; + obj = {"name": obj.name, "sitedir": obj.site_dir, "username": "", "password": ""}; } var form_directory = bt.open({ type: 1, @@ -5120,10 +5870,10 @@ var site = { "" + "
                                      " }); - $('.btn-colse-guard').click(function() { + $('.btn-colse-guard').click(function () { form_directory.close(); }); - $('.btn-submit-guard').click(function() { + $('.btn-submit-guard').click(function () { var guardData = {}; guardData['id'] = id; guardData['name'] = $('input[name="dir_name"]').val(); @@ -5131,7 +5881,7 @@ var site = { guardData['username'] = $('input[name="dir_username"]').val(); guardData['password'] = $('input[name="dir_password"]').val(); if (type) { - bt.site.create_dir_guard(guardData, function(rdata) { + bt.site.create_dir_guard(guardData, function (rdata) { if (rdata.status) { form_directory.close(); site.reload() @@ -5139,7 +5889,7 @@ var site = { bt.msg(rdata); }); } else { - bt.site.edit_dir_account(guardData, function(rdata) { + bt.site.edit_dir_account(guardData, function (rdata) { if (rdata.status) { form_directory.close(); site.reload() @@ -5148,7 +5898,7 @@ var site = { }); } }); - setTimeout(function() { + setTimeout(function () { if (!type) { $('input[name="dir_name"]').attr('disabled', 'disabled'); $('input[name="dir_sitedir"]').attr('disabled', 'disabled'); @@ -5156,12 +5906,12 @@ var site = { }, 500) }, - template_php: function(website,obj) { + template_php: function (website, obj) { var _type = 'add', _name = '', _bggrey = ''; if (obj == undefined) { - obj = { "name": "", "suffix": "php|jsp", "dir": "" }; + obj = {"name": "", "suffix": "php|jsp", "dir": ""}; } else { - obj = { "name": obj.name, "suffix": obj.suffix, "dir": obj.dir }; + obj = {"name": obj.name, "suffix": obj.suffix, "dir": obj.dir}; _type = 'edit'; _name = ' readonly'; _bggrey = 'background: #eee;' @@ -5171,11 +5921,11 @@ var site = { area: '440px', title: 'Deny access', closeBtn: 2, - btn: ['Save','Cancel'], + btn: ['Save', 'Cancel'], content: "
                                      " + "
                                      " + "Name" + - "
                                      " + + "
                                      " + "
                                      " + "
                                      " + "Suffix" + @@ -5196,7 +5946,9 @@ var site = { var dent_data = $('.php_deny').serializeObject(); dent_data.act = _type; dent_data.website = website; - bt.site.edit_php_deny(dent_data, function(rdata) { + var loading = bt.load(); + bt.site.edit_php_deny(dent_data, function (rdata) { + loading.close(); if (rdata.status) { form_directory.close(); site.reload(); @@ -5207,31 +5959,36 @@ var site = { } }); }, - del_php_deny: function(website,deny_name) { - layer.confirm('Are you sure to delete [ '+deny_name+' ] this deny?', { + del_php_deny: function (website, deny_name, callback) { + layer.confirm('Are you sure to delete [ ' + deny_name + ' ] this deny?', { icon: 0, closeBtn: 2, title: 'Delete deny', }, function (index) { - bt.site.del_php_deny({website:website,deny_name:deny_name}, function(rdata) { - if (rdata.status) { - layer.close(index); - site.reload() - } - bt.msg(rdata); + bt.site.del_php_deny({website: website, deny_name: deny_name}, function (rdata) { + layer.close(index); + if (callback) callback(rdata); }); }); }, - set_301_old: function(web) { - bt.site.get_domains(web.id, function(rdata) { - var domains = [{ title: lan.site.site, value: 'all' }]; - for (var i = 0; i < rdata.length; i++) domains.push({ title: rdata[i].name, value: rdata[i].name }); + set_301_old: function (web) { + bt.site.get_domains(web.id, function (rdata) { + var domains = [{title: lan.site.site, value: 'all'}]; + for (var i = 0; i < rdata.length; i++) domains.push({title: rdata[i].name, value: rdata[i].name}); - bt.site.get_site_301(web.name, function(pdata) { + bt.site.get_site_301(web.name, function (pdata) { var _val = pdata.src == '' ? 'all' : pdata.src var datas = [ - { title: lan.site.access_domain, width: '360px', name: 'domains', value: _val, disabled: pdata.status, type: 'select', items: domains }, - { title: lan.site.target_url, width: '360px', name: 'toUrl', value: pdata.url }, + { + title: lan.site.access_domain, + width: '360px', + name: 'domains', + value: _val, + disabled: pdata.status, + type: 'select', + items: domains + }, + {title: lan.site.target_url, width: '360px', name: 'toUrl', value: pdata.url}, { title: ' ', text: lan.site.enable_301, @@ -5239,8 +5996,8 @@ var site = { name: 'status', class: 'label-input-group', type: 'checkbox', - callback: function(sdata) { - bt.site.set_site_301(web.name, sdata.domains, sdata.toUrl, sdata.status ? '1' : '0', function(ret) { + callback: function (sdata) { + bt.site.set_site_301(web.name, sdata.domains, sdata.toUrl, sdata.status ? '1' : '0', function (ret) { if (ret.status) site.reload(10) bt.msg(ret); }) @@ -5257,139 +6014,182 @@ var site = { }) }) }, - set_301: function (web){ + set_301: function (web) { $('#webedit-con').html('
                                      '); bt_tools.table({ - el:'#redirect_list', - url:'/site?action=GetRedirectList', - param:{sitename:web.name}, - dataFilter:function(res){ - return {data:res}; + el: '#redirect_list', + url: '/site?action=GetRedirectList', + param: {sitename: web.name}, + dataFilter: function (res) { + return {data: res}; }, - column:[ - {type:'checkbox',width:20}, - {fid:'sitename',title:lan.site.redirect_type,type:'text',template:function(row){ - if (row.domainorpath == 'path') { - conter = row.redirectpath; - } else { - conter = row.redirectdomain ? row.redirectdomain.join('、') : lan.site.empty + column: [ + {type: 'checkbox', width: 20}, + { + fid: 'sitename', title: lan.site.redirect_type, type: 'text', template: function (row) { + if (row.domainorpath == 'path') { + conter = row.redirectpath; + } else { + conter = row.redirectdomain ? row.redirectdomain.join('、') : lan.site.empty + } + return '' + conter + ''; } - return '' + conter + ''; - }}, - {fid:'redirecttype',title:lan.site.redirect_mode,type:'text'}, - {fid:'holdpath',title:lan.site.reserve_url,config:{icon:false,list:[[1,lan.site.turn_on,'bt_success'],[0,lan.site.turn_off,'bt_danger']]},type:'status', - event:function(row,index,ev,key,that){ - row.holdpath = !row.holdpath?1:0; + }, + {fid: 'redirecttype', title: lan.site.redirect_mode, type: 'text'}, + { + fid: 'holdpath', + title: lan.site.reserve_url, + config: { + icon: false, + list: [[1, lan.site.turn_on, 'bt_success'], [0, lan.site.turn_off, 'bt_danger']] + }, + type: 'status', + event: function (row, index, ev, key, that) { + row.holdpath = row.holdpath == 0 ? 1 : 0; row.redirectdomain = JSON.stringify(row['redirectdomain']); - bt.site.modify_redirect(row,function (res){ + bt.site.modify_redirect(row, function (res) { row.redirectdomain = JSON.parse(row['redirectdomain']); - that.$modify_row_data({holdpath:row.holdpath}); + that.$modify_row_data({holdpath: row.holdpath}); bt.msg(res); }); } }, - {fid:'type',title:lan.site.status,config:{icon:true,list:[[1,lan.site.running_text,'bt_success','glyphicon-play'],[0,lan.site.already_stop,'bt_danger','glyphicon-pause']]},type:'status', - event:function(row,index,ev,key,that){ - row.type = !row.type?1:0; + { + fid: 'type', + title: lan.site.status, + config: { + icon: true, + list: [[1, lan.site.running_text, 'bt_success', 'glyphicon-play'], [0, lan.site.already_stop, 'bt_danger', 'glyphicon-pause']] + }, + type: 'status', + event: function (row, index, ev, key, that) { + row.type = row.type == 0 ? 1 : 0; row.redirectdomain = JSON.stringify(row['redirectdomain']); - bt.site.modify_redirect(row,function (res){ + bt.site.modify_redirect(row, function (res) { row.redirectdomain = JSON.parse(row['redirectdomain']); - that.$modify_row_data({holdpath:row.type}); + that.$modify_row_data({ status: row.type }); bt.msg(res); }); } - },{title:lan.site.operate,width:129,type:'group',align:'right',group:[{ - title:'Conf', - event:function(row,index,ev,key,that){ - bt.site.get_redirect_config({ - sitename: web.name, - redirectname: row.redirectname, - webserver: bt.get_cookie('serverType') - }, function (rdata) { - if (typeof rdata == 'object' && rdata.constructor == Array) { - if (!rdata[0].status) bt.msg(rdata) - } else { - if (!rdata.status) bt.msg(rdata) - } - var datas = [ - { items: [{ name: 'redirect_configs', type: 'textarea', value: rdata[0].data, widht: '340px', height: '200px' }] }, - { - name: 'btn_config_submit', text: 'Save', type: 'button', callback: function (ddata) { - bt.site.save_redirect_config({ path: rdata[1], data: editor.getValue(), encoding: rdata[0].encoding }, function (ret) { - if (ret.status) { - site.reload(11); - redirect_config.close(); - } - bt.msg(ret); - }) - } + }, { + title: lan.site.operate, width: 129, type: 'group', align: 'right', group: [{ + title: 'Conf', + event: function (row, index, ev, key, that) { + bt.site.get_redirect_config({ + sitename: web.name, + redirectname: row.redirectname, + webserver: bt.get_cookie('serverType') + }, function (rdata) { + if (typeof rdata == 'object' && rdata.constructor == Array) { + if (!rdata[0].status) bt.msg(rdata) + } else { + if (!rdata.status) bt.msg(rdata) } - ] - redirect_config = bt.open({ - type: 1, - area: ['550px', '550px'], - title: 'Edit profile [' + row.redirectname + ']', - closeBtn: 2, - shift: 0, - content: "
                                      " - }) - var robj = $('#redirect_config_con'); - for (var i = 0; i < datas.length; i++) { - var _form_data = bt.render_form_line(datas[i]); - robj.append(_form_data.html); - bt.render_clicks(_form_data.clicks); - } - robj.append(bt.render_help(['This is the configuration file of the load balancing. Not modify if you do not understand the configuration rules.'])); - $('textarea.redirect_configs').attr('id', 'configBody'); - var editor = CodeMirror.fromTextArea(document.getElementById("configBody"), { - extraKeys: { "Ctrl-Space": "autocomplete" }, - lineNumbers: true, - matchBrackets: true + var datas = [ + { + items: [{ + name: 'redirect_configs', + type: 'textarea', + value: rdata[0].data, + widht: '340px', + height: '200px' + }] + }, + { + name: 'btn_config_submit', + text: 'Save', + type: 'button', + callback: function (ddata) { + bt.site.save_redirect_config({ + path: rdata[1], + data: editor.getValue(), + encoding: rdata[0].encoding + }, function (ret) { + if (ret.status) { + site.reload(11); + redirect_config.close(); + } + bt.msg(ret); + }) + } + } + ] + redirect_config = bt.open({ + type: 1, + area: ['550px', '550px'], + title: 'Edit profile [' + row.redirectname + ']', + closeBtn: 2, + shift: 0, + content: "
                                      " + }) + var robj = $('#redirect_config_con'); + for (var i = 0; i < datas.length; i++) { + var _form_data = bt.render_form_line(datas[i]); + robj.append(_form_data.html); + bt.render_clicks(_form_data.clicks); + } + robj.append(bt.render_help(['This is the configuration file of the load balancing. Not modify if you do not understand the configuration rules.'])); + $('textarea.redirect_configs').attr('id', 'configBody'); + var editor = CodeMirror.fromTextArea(document.getElementById("configBody"), { + extraKeys: {"Ctrl-Space": "autocomplete"}, + lineNumbers: true, + matchBrackets: true + }); + $(".CodeMirror-scroll").css({"height": "350px", "margin": 0, "padding": 0}); + setTimeout(function () { + editor.refresh(); + }, 250); }); - $(".CodeMirror-scroll").css({ "height": "350px", "margin": 0, "padding": 0 }); - setTimeout(function () { - editor.refresh(); - }, 250); - }); - } - },{ - title:lan.site.edit, - event:function(row,index,ev,key,that){ - site.edit.templet_301(web.name,web.id,false,row); - } - },{ - title:lan.site.del, - event:function(row,index,ev,key,that){ - bt.site.remove_redirect(web.name,row.redirectname,function(rdata){ - if(rdata.status) that.$delete_table_row(index); - }); + } + }, { + title: lan.site.edit, + event: function (row, index, ev, key, that) { + site.edit.templet_301(web.name, web.id, false, row); + } + }, { + title: lan.site.del, + event: function (row, index, ev, key, that) { + bt.site.remove_redirect(web.name, row.redirectname, function (rdata) { + if (rdata.status) that.$delete_table_row(index); + }); + } + }] + }], + tootls: [{ //按钮组 + type: 'group', + positon: ['left', 'top'], + list: [{ + title: 'Add redirection', active: true, event: function (ev) { + site.edit.templet_301(web.name, web.id, true); } }] - }], - tootls:[{ //按钮组 - type:'group', - positon:['left','top'], - list:[{title:'Add redirection',active:true, event:function(ev){ - site.edit.templet_301(web.name,web.id,true); - }}] - },{ //批量操作 - type:'batch', - positon:['left','bottom'], - config:{ - title:' delete', - url:'/site?action=del_redirect_multiple', - param:{site_id:web.id}, - paramId:'redirectname', - paramName:'redirectnames', - theadName:'Name', - confirmVerify:false // 是否提示验证方式 + }, { //批量操作 + type: 'batch', + positon: ['left', 'bottom'], + config: { + title: ' delete', + url: '/site?action=del_redirect_multiple', + param: {site_id: web.id}, + paramId: 'redirectname', + paramName: 'redirectnames', + theadName: 'Name', + confirmVerify: false // 是否提示验证方式 } }] }); }, - templet_proxy: function(sitename, type, obj) { + templet_proxy: function (sitename, type, obj) { if (type) { - obj = { "type": 1, "cache": 0, "proxyname": "", "proxydir": "/", "proxysite": "http://", "cachetime": 1, "todomain": "$host", "subfilter": [{ "sub1": "", "sub2": "" }] }; + obj = { + "type": 1, + "cache": 0, + "proxyname": "", + "proxydir": "/", + "proxysite": "http://", + "cachetime": 1, + "todomain": "$host", + "subfilter": [{"sub1": "", "sub2": ""}] + }; } var sub_conter = ''; for (var i = 0; i < obj.subfilter.length; i++) { @@ -5453,7 +6253,7 @@ var site = { "
                                      " + "" + lan.site.proxy_domain + "" + "
                                      " + - ""+ + "" + "
                                      " + "
                                      " + "
                                      " + @@ -5470,7 +6270,7 @@ var site = { "" }); bt.set_cookie('form_proxy', form_proxy); - $('.add-replace-prosy').click(function() { + $('.add-replace-prosy').click(function () { var length = $(".replace_conter .sub-groud").length; if (length == 2) $(this).attr('disabled', 'disabled') var conter = "
                                      " + @@ -5480,41 +6280,42 @@ var site = { "
                                      " $(".replace_conter .info-r").append(conter); }); - $('[name="proxysite"]').keyup(function() { + $('[name="proxysite"]').keyup(function () { var val = $(this).val(), ip_reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/; val = val.replace(/^http[s]?:\/\//, ''); - val = val.replace(/:([0-9]*)$/, ''); + // val = val.replace(/:([0-9]*)$/, ''); + val = val.replace(/(:|\?|\/|\\)(.*)$/, ''); if (ip_reg.test(val)) { $("[name='todomain']").val('$host'); } else { $("[name='todomain']").val(val); } }); - $('#openAdvanced').click(function() { + $('#openAdvanced').click(function () { if ($(this).prop('checked')) { $('.advanced').show(); } else { $('.advanced').hide(); } }); - $('#openNginx').click(function() { + $('#openNginx').click(function () { if ($(this).prop('checked')) { $('.cachetime').show(); } else { $('.cachetime').hide(); } }); - $('.btn-colse-prosy').click(function() { + $('.btn-colse-prosy').click(function () { form_proxy.close(); }); - $('.replace_conter').on('click', '.proxy_del_sub', function() { + $('.replace_conter').on('click', '.proxy_del_sub', function () { $(this).parent().remove(); $('.add-replace-prosy').removeAttr('disabled') }); - $(".btn-submit-prosy").click(function() { + $(".btn-submit-prosy").click(function () { var form_proxy_data = {}; - $.each($('#form_proxy').serializeArray(), function() { + $.each($('#form_proxy').serializeArray(), function () { if (form_proxy_data[this.name]) { if (!form_proxy_data[this.name].push) { form_proxy_data[this.name] = [form_proxy_data[this.name]]; @@ -5529,9 +6330,9 @@ var site = { form_proxy_data['advanced'] = (form_proxy_data['advanced'] == undefined ? 0 : 1); form_proxy_data['sitename'] = sitename; form_proxy_data['subfilter'] = JSON.stringify([ - { 'sub1': form_proxy_data['rep1'] || '', 'sub2': form_proxy_data['rep2'] || '' }, - { 'sub1': form_proxy_data['rep3'] || '', 'sub2': form_proxy_data['rep4'] || '' }, - { 'sub1': form_proxy_data['rep5'] || '', 'sub2': form_proxy_data['rep6'] || '' }, + {'sub1': form_proxy_data['rep1'] || '', 'sub2': form_proxy_data['rep2'] || ''}, + {'sub1': form_proxy_data['rep3'] || '', 'sub2': form_proxy_data['rep4'] || ''}, + {'sub1': form_proxy_data['rep5'] || '', 'sub2': form_proxy_data['rep6'] || ''}, ]); for (var i in form_proxy_data) { if (i.indexOf('rep') != -1) { @@ -5539,7 +6340,7 @@ var site = { } } if (type) { - bt.site.create_proxy(form_proxy_data, function(rdata) { + bt.site.create_proxy(form_proxy_data, function (rdata) { if (rdata.status) { form_proxy.close(); site.reload(12); @@ -5547,7 +6348,7 @@ var site = { bt.msg(rdata); }); } else { - bt.site.modify_proxy(form_proxy_data, function(rdata) { + bt.site.modify_proxy(form_proxy_data, function (rdata) { if (rdata.status) { form_proxy.close(); site.reload(12); @@ -5558,175 +6359,227 @@ var site = { }); }, set_proxy: function (web) { - var limit_len = bt.get_cookie('serverType') == 'nginx'?'proxy_list_limit_4':'proxy_list_limit_3'; - $('#webedit-con').html('
                                      '); + var limit_len = bt.get_cookie('serverType') == 'nginx' ? 'proxy_list_limit_4' : 'proxy_list_limit_3'; + $('#webedit-con').html('
                                      '); String.prototype.myReplace = function (f, e) {//吧f替换成e var reg = new RegExp(f, "g"); //创建正则RegExp对象 return this.replace(reg, e); } bt_tools.table({ - el:'#proxy_list', - url:'/site?action=GetProxyList', - param:{sitename:web.name}, - dataFilter:function(res){ - return {data:res}; + el: '#proxy_list', + url: '/site?action=GetProxyList', + param: {sitename: web.name}, + dataFilter: function (res) { + return {data: res}; }, - column:[ - {type:'checkbox',width:20}, - {fid:'proxyname',title:lan.site.name,template:function (row,index) { - return ''+ row.proxyname +''; - }}, - {fid:'proxydir',title:lan.site.proxy_dir,template:function (row,index) { - return ''+ row.proxydir +''; - }}, - {fid:'proxysite',title:lan.site.target_url,type:'link',href:true}, - bt.get_cookie('serverType') == 'nginx' ? {fid:'cache',title:lan.site.cache,config:{icon:false,list:[[1,lan.site.already_open,'bt_success'],[0,lan.site.already_close,'bt_danger']]},type:'status',event:function(row,index,ev,key,that) { - row['cache'] = !row['cache']?1:0; - row['subfilter'] = JSON.stringify(row['subfilter']); - bt.site.modify_proxy(row, function (rdata) { - row['subfilter'] = JSON.parse(row['subfilter']); - if (rdata.status) that.$modify_row_data({cache:row['cache']}); - bt.msg(rdata); - }); - }}: {}, - {fid:'type',title:lan.site.status,config:{icon:true,list:[[1,lan.site.running_text,'bt_success','glyphicon-play'],[0,lan.site.already_stop,'bt_danger','glyphicon-pause']]},type:'status',event:function(row,index,ev,key,that){ - row['type'] = !row['type']?1:0; - row['subfilter'] = JSON.stringify(row['subfilter']); - bt.site.modify_proxy(row, function (rdata) { - row['subfilter'] = JSON.parse(row['subfilter']); - if (rdata.status) that.$modify_row_data({type:row['type']}); - bt.msg(rdata); - }); - }}, - {title:lan.site.operate,width:115,type:'group',align:'right',group:[{ - title:'Conf', - event:function(row,index,ev,key,that){ - bt.site.get_proxy_config({ - sitename: web.name, - proxyname: row.proxyname, - webserver: bt.get_cookie('serverType') - }, function (rdata) { - if (typeof rdata == 'object' && rdata.constructor == Array) { - if (!rdata[0].status) bt.msg(rdata) - } else { - if (!rdata.status) bt.msg(rdata) - } - var datas = [ - { items: [{ name: 'proxy_configs', type: 'textarea', value: rdata[0].data, widht: '340px', height: '200px' }] }, - { - name: 'btn_config_submit', text: 'Save', type: 'button', callback: function (ddata) { - bt.site.save_proxy_config({ path: rdata[1], data: editor.getValue(), encoding: rdata[0].encoding }, function (ret) { - if (ret.status) { - site.reload(12); - proxy_config.close(); - } - bt.msg(ret); - }) - } - } - ] - proxy_config = bt.open({ - type: 1, - area: ['550px', '550px'], - title: 'Edit profile [' + row.proxyname + ']', - closeBtn: 2, - shift: 0, - content: "
                                      " - }) - var robj = $('#proxy_config_con'); - for (var i = 0; i < datas.length; i++) { - var _form_data = bt.render_form_line(datas[i]); - robj.append(_form_data.html); - bt.render_clicks(_form_data.clicks); - } - robj.append(bt.render_help(['This is the configuration file of the load balancing. Not modify if you do not understand the configuration rules.'])); - $('textarea.proxy_configs').attr('id', 'configBody'); - var editor = CodeMirror.fromTextArea(document.getElementById("configBody"), { - extraKeys: { "Ctrl-Space": "autocomplete" }, - lineNumbers: true, - matchBrackets: true - }); - $(".CodeMirror-scroll").css({ "height": "350px", "margin": 0, "padding": 0 }); - setTimeout(function () { - editor.refresh(); - }, 250); + column: [ + {type: 'checkbox', width: 20}, + { + fid: 'proxyname', title: lan.site.name, template: function (row, index) { + return '' + row.proxyname + ''; + } + }, + { + fid: 'proxydir', title: lan.site.proxy_dir, template: function (row, index) { + return '' + row.proxydir + ''; + } + }, + {fid: 'proxysite', title: lan.site.target_url, type: 'link', href: true}, + bt.get_cookie('serverType') == 'nginx' ? { + fid: 'cache', + title: lan.site.cache, + config: { + icon: false, + list: [[1, lan.site.already_open, 'bt_success'], [0, lan.site.already_close, 'bt_danger']] + }, + type: 'status', + event: function (row, index, ev, key, that) { + row['cache'] = !row['cache'] ? 1 : 0; + row['subfilter'] = JSON.stringify(row['subfilter']); + bt.site.modify_proxy(row, function (rdata) { + row['subfilter'] = JSON.parse(row['subfilter']); + if (rdata.status) that.$modify_row_data({cache: row['cache']}); + bt.msg(rdata); }); } - },{ - title:'Edit', - event:function(row,index,ev,key,that){ - site.edit.templet_proxy(web.name,false,row); + } : {}, + { + fid: 'type', + title: lan.site.status, + config: { + icon: true, + list: [[1, lan.site.running_text, 'bt_success', 'glyphicon-play'], [0, lan.site.already_stop, 'bt_danger', 'glyphicon-pause']] + }, + type: 'status', + event: function (row, index, ev, key, that) { + row['type'] = !row['type'] ? 1 : 0; + row['subfilter'] = JSON.stringify(row['subfilter']); + bt.site.modify_proxy(row, function (rdata) { + row['subfilter'] = JSON.parse(row['subfilter']); + if (rdata.status) that.$modify_row_data({type: row['type']}); + bt.msg(rdata); + }); } - },{ - title:'Del', - event:function(row,index,ev,key,that){ - bt.site.remove_proxy(web.name,row.proxyname,function(rdata){ - if(rdata.status) that.$delete_table_row(index); - }) + }, + { + title: lan.site.operate, width: 115, type: 'group', align: 'right', group: [{ + title: 'Conf', + event: function (row, index, ev, key, that) { + bt.site.get_proxy_config({ + sitename: web.name, + proxyname: row.proxyname, + webserver: bt.get_cookie('serverType') + }, function (rdata) { + if (typeof rdata == 'object' && rdata.constructor == Array) { + if (!rdata[0].status) bt.msg(rdata) + } else { + if (!rdata.status) bt.msg(rdata) + } + var datas = [ + { + items: [{ + name: 'proxy_configs', + type: 'textarea', + value: rdata[0].data, + widht: '340px', + height: '200px' + }] + }, + { + name: 'btn_config_submit', + text: 'Save', + type: 'button', + callback: function (ddata) { + bt.site.save_proxy_config({ + path: rdata[1], + data: editor.getValue(), + encoding: rdata[0].encoding + }, function (ret) { + if (ret.status) { + site.reload(12); + proxy_config.close(); + } + bt.msg(ret); + }) + } + } + ] + proxy_config = bt.open({ + type: 1, + area: ['550px', '550px'], + title: 'Edit profile [' + row.proxyname + ']', + closeBtn: 2, + shift: 0, + content: "
                                      " + }) + var robj = $('#proxy_config_con'); + for (var i = 0; i < datas.length; i++) { + var _form_data = bt.render_form_line(datas[i]); + robj.append(_form_data.html); + bt.render_clicks(_form_data.clicks); + } + robj.append(bt.render_help(['This is the configuration file of the load balancing. Not modify if you do not understand the configuration rules.'])); + $('textarea.proxy_configs').attr('id', 'configBody'); + var editor = CodeMirror.fromTextArea(document.getElementById("configBody"), { + extraKeys: {"Ctrl-Space": "autocomplete"}, + lineNumbers: true, + matchBrackets: true + }); + $(".CodeMirror-scroll").css({"height": "350px", "margin": 0, "padding": 0}); + setTimeout(function () { + editor.refresh(); + }, 250); + }); + } + }, { + title: 'Edit', + event: function (row, index, ev, key, that) { + site.edit.templet_proxy(web.name, false, row); + } + }, { + title: 'Del', + event: function (row, index, ev, key, that) { + bt.site.remove_proxy(web.name, row.proxyname, function (rdata) { + if (rdata.status) that.$delete_table_row(index); + }) + } + }] + }], + tootls: [{ //按钮组 + type: 'group', + positon: ['left', 'top'], + list: [{ + title: 'Add reverse proxy', active: true, event: function (ev) { + site.edit.templet_proxy(web.name, true) } }] - }], - tootls:[{ //按钮组 - type:'group', - positon:['left','top'], - list:[{title:'Add reverse proxy',active:true, event:function(ev){ - site.edit.templet_proxy(web.name, true) - }}] - },{ //批量操作 - type:'batch', - positon:['left','bottom'], - config:{ - title:' delete', - url:'/site?action=del_proxy_multiple', - param:{site_id:web.id}, - paramId:'proxyname', - paramName:'proxynames', - theadName:'Name', - confirmVerify:false // 是否提示验证方式 + }, { //批量操作 + type: 'batch', + positon: ['left', 'bottom'], + config: { + title: ' delete', + url: '/site?action=del_proxy_multiple', + param: {site_id: web.id}, + paramId: 'proxyname', + paramName: 'proxynames', + theadName: 'Name', + confirmVerify: false // 是否提示验证方式 } }] }); }, - set_security: function(web) { - bt.site.get_site_security(web.id, web.name, function(rdata) { + set_security: function (web) { + bt.site.get_site_security(web.id, web.name, function (rdata) { var robj = $('#webedit-con'); var datas = [ - { title: lan.site.url_suffix, name: 'sec_fix', value: rdata.fix, disabled: rdata.status, width: '300px' }, + { + title: lan.site.url_suffix, + name: 'sec_fix', + value: rdata.fix, + disabled: rdata.status, + width: '300px' + }, { title: lan.site.access_domain1, items: [{ - text: lan.site.start_anti_leech, - name: 'sec_domains', - width: '300px', - height:'210px', - disabled: rdata.status, - value: rdata.domains.replace(/,/g,"\n"), - type: 'textarea' + text: lan.site.start_anti_leech, + name: 'sec_domains', + width: '300px', + height: '210px', + disabled: rdata.status, + value: rdata.domains.replace(/,/g, "\n"), + type: 'textarea' }] }, - { title: 'Response', name: 'return_rule', value: rdata.return_rule, disabled: rdata.status, width: '300px' }, + { + title: 'Response', + name: 'return_rule', + value: rdata.return_rule, + disabled: rdata.status, + width: '300px' + }, { title: ' ', class: 'label-input-group', items: [{ - text: lan.site.start_anti_leech, - name: 'status', - value: rdata.status, - type: 'checkbox', - callback: function(sdata) { - bt.site.set_site_security(web.id, web.name, sdata.sec_fix, sdata.sec_domains.split("\n").join(','), sdata.status, sdata.return_rule,function(ret) { - if (ret.status) site.reload(13) - bt.msg(ret); - }) - } - }, + text: lan.site.start_anti_leech, + name: 'status', + value: rdata.status, + type: 'checkbox', + callback: function (sdata) { + bt.site.set_site_security(web.id, web.name, sdata.sec_fix, sdata.sec_domains.split("\n").join(','), sdata.status, sdata.return_rule, function (ret) { + if (ret.status) site.reload(13) + bt.msg(ret); + }) + } + }, { text: 'Allow empty HTTP_REFERER requests', name: 'none', value: rdata.none, type: 'checkbox', - callback: function(sdata) { - bt.site.set_site_security(web.id, web.name, sdata.sec_fix, sdata.sec_domains.split("\n").join(','), '1',sdata.return_rule, function(ret) { + callback: function (sdata) { + bt.site.set_site_security(web.id, web.name, sdata.sec_fix, sdata.sec_domains.split("\n").join(','), '1', sdata.return_rule, function (ret) { if (ret.status) site.reload(13) bt.msg(ret); }) @@ -5741,18 +6594,18 @@ var site = { robj.append(_form_data.html); bt.render_clicks(_form_data.clicks); } - robj.find("#none").css('margin-top','10px') + robj.find("#none").css('margin-top', '10px') $("#none").before("
                                      "); var helps = [lan.site.access_empty_ref_default, lan.site.multi_url, lan.site.trigger_return_404] robj.append(bt.render_help(helps)); }) }, - set_tomact: function(web) { - bt.site.get_site_phpversion(web.name, function(rdata) { + set_tomact: function (web) { + bt.site.get_site_phpversion(web.name, function (rdata) { var robj = $('#webedit-con'); if (!rdata.tomcatversion) { robj.html('' + lan.site.tomcat_err_msg1 + ''); - layer.msg(lan.site.tomcat_err_msg, { icon: 2 }); + layer.msg(lan.site.tomcat_err_msg, {icon: 2}); return; } var data = { @@ -5762,8 +6615,8 @@ var site = { name: 'tomcat', value: rdata.tomcat == -1 ? false : true, type: 'checkbox', - callback: function(sdata) { - bt.site.set_tomcat(web.name, function(ret) { + callback: function (sdata) { + bt.site.set_tomcat(web.name, function (ret) { if (ret.status) site.reload(9) bt.msg(ret); }) @@ -5777,29 +6630,185 @@ var site = { robj.append(bt.render_help(helps)); }) }, - get_site_logs: function(web) { - $('#webedit-con').append('
                                      ') - var serverType = bt.get_cookie('serverType'),shell = 'tail -n 100 -f /www/wwwlogs/'+ web.name; + get_site_logs: function (web) { + $('#webedit-con').append('
                                      ') + var serverType = bt.get_cookie('serverType'), shell = 'tail -n 100 -f /www/wwwlogs/' + web.name; var _tab = [{ - title: "Access log", - on: true, - callback:function(robj){ - var shellCopy = shell + (serverType === 'nginx'?'.':serverType === 'apache'?'-access_':'_ols.access_') + 'log'; - bt_tools.command_line_output({ el:'#webedit-con .tab-con', shell:shellCopy,area:['100%','580px']}) - } - },{ - title: "Error log", - callback:function(robj){ - var shellCopy = shell + (serverType === 'nginx'?'.error.':serverType === 'apache'?'-error_':'_ols.error_') + 'log'; - bt_tools.command_line_output({ el:'#webedit-con .tab-con', shell:shellCopy,area:['100%','580px']}) - } - }] - bt.render_tab('tabLogs',_tab); - $('#tabLogs span:eq(0)').click(); + title: "Access log", + on: true, + callback: function (robj) { + bt.site.get_site_logs(web.name, function (rdata) { + var _logs_info = $('
                                      ').text(rdata.msg) + var logs = { class: 'bt-logs', items: [{ name: 'site_logs', height: '560px', value: _logs_info.html(), width: '100%', type: 'textarea' }] }, + _form_data = bt.render_form_line(logs); + robj.append(_form_data.html); + bt.render_clicks(_form_data.clicks); + $('textarea[name="site_logs"]').attr('readonly', true) + $('textarea[name="site_logs"]').scrollTop(100000000000) + }) + } + }, { + title: "Error log", + callback: function (robj) { + bt.site.get_site_error_logs(web.name, function (rdata) { + var _logs_info = $('
                                      ').text(rdata.msg) + var logs = { class: 'bt-logs', items: [{ name: 'site_logs', height: '560px', value: _logs_info.html(), width: '100%', type: 'textarea' }] }, + _form_data = bt.render_form_line(logs); + robj.append(_form_data.html); + bt.render_clicks(_form_data.clicks); + $('textarea[name="site_logs"]').attr('readonly', true) + $('textarea[name="site_logs"]').scrollTop(100000000000) + }) + } + },{ + title:"Log Security Analysis", + callback: function(robj){ + var _serverType = bt.get_cookie('serverType'), + pathFile = '', + progress = '', //扫描进度 + loadT = bt.load('Getting log analytics data, please wait...'); + + switch(_serverType){ + case 'nginx': + pathFile = web.name+'.log' + break; + case 'apache': + pathFile = web.name+'-access_log' + break; + default: + pathFile = web.name+'_ols.access_log' + break; + } + $.post('/ajax?action=get_result&path=/www/wwwlogs/' + pathFile, function (rdata) { + loadT.close(); + //1.扫描按钮 + var analyes_log_btn = '' + + //2.功能介绍 + var analyse_help = '
                                        \ +
                                      • Log analysis: Scan the logs(/www/wwwroot/.log) for requests with attack (types include:xss,sql,san,php)
                                      • \ +
                                      • Analyzed log data contains intercepted requests
                                      • \ +
                                      • By default, the last scan data is displayed (if not, please click log scan)
                                      • \ +
                                      • If the log file is too large, scanning may take a long time, please be patient
                                      • \ +
                                      • aaPanel WAF can effectively block such attacks
                                      • \ +
                                      ' + + robj.append(analyes_log_btn+'
                                      '+analyse_help) + render_analyse_list(rdata); + + //事件 + $(robj).find('.analyes_log').click(function(){ + bt.confirm({ + title:'Scan website logs', + msg:'It is recommended to perform security analysis when the server load is low. This time, the ['+web.name+'.log] file will be scanned. It may take a long time. Do you want to continue?' + }, function(index){ + layer.close(index) + progress = layer.open({ + type: 1, + closeBtn: 2, + title: false, + shade: 0, + area: '400px', + content: '
                                      Scanning, scanning progress...
                                      \ +
                                      \ +
                                      0%
                                      \ +
                                      \ +
                                      ', + success:function(){ + // 开启扫描并且持续获取进度 + $.post('/ajax?action=log_analysis&path=/www/wwwlogs/' + pathFile, function (rdata) { + if(rdata.status){ + detect_progress(); + }else{ + layer.close(progress); + layer.msg(rdata.msg, { icon: 2, time: 0, shade: 0.3, shadeClose: true }); + } + }) + } + }) + }) + }) + }) + // 渲染分析日志列表 + function render_analyse_list(rdata){ + var analyse_list = '
                                      \ + \ + ' + if(rdata.is_status){ //检测是否有扫描数据 + analyse_list +='\ + \ + \ + \ + \ + \ + \ + \ + \ + ' + }else{ + analyse_list+='' + } + analyse_list += '
                                      DateTimeXSSSQLSacnPHPIP(top100)URL(top100)
                                      '+rdata.start_time+''+rdata.time.substring(0,4)+' Sec0?'style="color:red"':'')+' name="xss">'+rdata.xss+'0?'style="color:red"':'')+' name="sql">'+rdata.sql+'0?'style="color:red"':'')+' name="san">'+rdata.san+'0?'style="color:red"':'')+' name="php">'+rdata.php+''+rdata.ip+''+rdata.url+'
                                      no scan data
                                      ' + $('.analyse_log_table').html(analyse_list) + $('.onChangeLogDatail').css('cursor','pointer').attr('title','Details') + //查看详情 + $('.onChangeLogDatail').on('click',function(){ + get_analysis_data_datail($(this).attr('name')) + }) + } + // 扫描进度 + function detect_progress(){ + $.post('/ajax?action=speed_log&path=/www/wwwlogs/' + pathFile, function (res) { + var pro = res.msg + if(pro !== 100){ + if (pro > 100) pro = 100; + if (pro !== NaN) { + $('.pro_style .progress-bar').css('width', pro + '%').html(pro + '%'); + } + setTimeout(function () { + detect_progress(); + }, 1000); + }else{ + layer.msg('Scan complete',{icon:1,timeout:4000}) + layer.close(progress); + get_analysis_data(); + } + }) + } + // 获取扫描结果 + function get_analysis_data(){ + var loadTGA = bt.load('Getting log analytics data, please wait...'); + $.post('/ajax?action=get_result&path=/www/wwwlogs/' + pathFile, function (rdata) { + loadTGA.close(); + render_analyse_list(rdata,true) + }) + } + // 获取扫描结果详情日志 + function get_analysis_data_datail(name){ + layer.open({ + type: 1, + closeBtn: 2, + shadeClose: false, + title: '[ '+name+' ] log details', + area: '650px', + content:'
                                      ',
                                      +                      success(){
                                      +                        var loadTGD = bt.load('Getting log details data, please wait...');
                                      +                        $.post('/ajax?action=get_detailed&path=/www/wwwlogs/' + pathFile+'&type='+name+'', function (logs) {
                                      +                          loadTGD.close();
                                      +                          $('#analysis_pre').text((name == 'ip' || name == 'url'?' [Access Times] ['+name+'] \n':'')+logs)
                                      +                        })
                                      +                      }
                                      +                    })
                                      +                  }
                                      +                }
                                      +            }]
                                      +            bt.render_tab('tabLogs', _tab);
                                      +            $('#tabLogs span:eq(0)').click();
                                               }
                                           },
                                      -    create_let: function(ddata, callback) {
                                      -        bt.site.create_let(ddata, function(ret) {
                                      +    create_let: function (ddata, callback) {
                                      +        bt.site.create_let(ddata, function (ret) {
                                                   if (ret.status) {
                                                       if (callback) {
                                                           callback(ret);
                                      @@ -5810,7 +6819,7 @@ var site = {
                                                       }
                                                   } else {
                                                       if (ret.msg) {
                                      -                    if (typeof(ret.msg) == 'string') {
                                      +                    if (typeof (ret.msg) == 'string') {
                                                               ret.msg = [ret.msg, ""];
                                                           }
                                                       }
                                      @@ -5826,23 +6835,23 @@ var site = {
                                                       if (ret.err[0].length > 10) data += '

                                      ' + ret.err[0].replace(/\n/g, '
                                      ') + '

                                      '; if (ret.err[1].length > 10) data += '

                                      ' + ret.err[1].replace(/\n/g, '
                                      ') + '

                                      '; - layer.msg(data, { icon: 2, area: '500px', time: 0, shade: 0.3, shadeClose: true }); + layer.msg(data, {icon: 2, area: '500px', time: 0, shade: 0.3, shadeClose: true}); } }) }, - reload: function(index) { + reload: function (index) { if (index == undefined) index = 0 var _sel = $('.site-menu p.bgw'); if (_sel.length == 0) _sel = $('.site-menu p:eq(0)'); _sel.trigger('click'); }, - plugin_firewall: function() { + plugin_firewall: function () { var typename = bt.get_cookie('serverType'); var name = 'btwaf_httpd'; if (typename == "nginx") name = 'btwaf' - bt.plugin.get_plugin_byhtml(name, function(rhtml) { + bt.plugin.get_plugin_byhtml(name, function (rhtml) { if (rhtml.status === false) return; var list = rhtml.split(' - -{% endblock %} \ No newline at end of file + {% endblock %} + {% block scripts %} + + + + + + + {% endblock %} \ No newline at end of file diff --git a/BTPanel/templates/default/control.html b/BTPanel/templates/default/control.html index 7451f278..f76cd980 100644 --- a/BTPanel/templates/default/control.html +++ b/BTPanel/templates/default/control.html @@ -141,6 +141,7 @@ {% endblock %} {% block scripts %} + {{ super() }} {% endblock %} diff --git a/BTPanel/templates/default/crontab.html b/BTPanel/templates/default/crontab.html index 0e57727d..dc3fbb70 100644 --- a/BTPanel/templates/default/crontab.html +++ b/BTPanel/templates/default/crontab.html @@ -180,20 +180,53 @@
                                      {% endblock %} {% block scripts %} - - +{{ super() }} + + {% endblock %} diff --git a/BTPanel/templates/default/database.html b/BTPanel/templates/default/database.html index db2954d2..7c7475ac 100644 --- a/BTPanel/templates/default/database.html +++ b/BTPanel/templates/default/database.html @@ -4,16 +4,9 @@
                                      -
                                      -
                                      - {{data['lan']['H1']}}/{{data['lan']['H2']}} -
                                      -
                                      {% endblock %} {% block scripts %} +{{ super() }} {% endblock %} \ No newline at end of file diff --git a/BTPanel/templates/default/down.html b/BTPanel/templates/default/down.html index cec123ae..ede2b10f 100644 --- a/BTPanel/templates/default/down.html +++ b/BTPanel/templates/default/down.html @@ -1017,7 +1017,7 @@ td {
                                      -
                                      Share file【 {{data.filename}}
                                      +
                                      Share file [ {{data.filename}} ]
                                      Term of validity:{{data.expire}}
                                      {% if not data['PATH'] %} @@ -1282,7 +1282,7 @@ td { layer.open({ type: 1, closeBtn: 1, - title: 'Preview pictures【'+fileName+'】', + title: 'Preview pictures [ '+fileName+' ]', area: area, shadeClose: true, content: '
                                      ' diff --git a/BTPanel/templates/default/error.html b/BTPanel/templates/default/error.html index 4242784b..f75fe853 100644 --- a/BTPanel/templates/default/error.html +++ b/BTPanel/templates/default/error.html @@ -7,7 +7,5 @@

                                      {1}

                                      {2}

                                      -
                                      -
                                      {3} {4}
                                      \ No newline at end of file diff --git a/BTPanel/templates/default/error3.html b/BTPanel/templates/default/error3.html index ef9dce4c..7f79dacc 100644 --- a/BTPanel/templates/default/error3.html +++ b/BTPanel/templates/default/error3.html @@ -1,15 +1,283 @@ {% extends "layout.html" %} {% block content %} +
                                      -
                                      -
                                      -
                                      -

                                      Sorry, you need to meet the following conditions to use:

                                      -

                                      Please upgrade Nginx firewall version to 7.8.0 or above

                                      -
                                      -
                                      -
                                      +
                                      + {% if 'error_msg' in data %} +
                                      +
                                      +
                                      aaPanel WAF
                                      +
                                      +
                                      +
                                      +
                                      +

                                      Tip: This page can be turned off in the panel settings

                                      +
                                      +
                                      +
                                      + Nginx WAF function introduction +
                                        +
                                      • Only supports Nginx
                                      • +
                                      • Defend against CC attacks
                                      • +
                                      • Keyword blocking
                                      • +
                                      • Block malicious scans
                                      • +
                                      • Stop hackers
                                      • +
                                      +
                                      + Buy now +
                                      +
                                      +
                                      +
                                        +
                                      • Overview
                                      • +
                                      • Report
                                      • +
                                      • Global
                                      • +
                                      • WebSite
                                      • +
                                      • Blockade
                                      • +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      + {% else %} +
                                      +
                                      +
                                      aaPanel WAF
                                      +
                                      +
                                      +
                                      +
                                      +

                                      Tip: This page can be turned off in the panel settings

                                      +
                                      +
                                      +
                                      + Nginx WAF function introduction +
                                        +
                                      • Only supports Nginx
                                      • +
                                      • Defend against CC attacks
                                      • +
                                      • Keyword blocking
                                      • +
                                      • Block malicious scans
                                      • +
                                      • Stop hackers
                                      • +
                                      +
                                      + Buy now +
                                      +
                                      +
                                      +
                                        +
                                      • Overview
                                      • +
                                      • Report
                                      • +
                                      • Global
                                      • +
                                      • WebSite
                                      • +
                                      • Blockade
                                      • +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      + {% endif %} +
                                      {% endblock %} {% block scripts %} + {{ super() }} + {% endblock %} \ No newline at end of file diff --git a/BTPanel/templates/default/files.html b/BTPanel/templates/default/files.html index 49442d96..9ef49480 100644 --- a/BTPanel/templates/default/files.html +++ b/BTPanel/templates/default/files.html @@ -38,7 +38,7 @@
                                      -
                                      +
                                      @@ -419,13 +419,19 @@ {% endblock %} {% block scripts %} - - - - - - - - - +{{ super() }} + + + + + + + + + + {% endblock %} \ No newline at end of file diff --git a/BTPanel/templates/default/firewall.html b/BTPanel/templates/default/firewall.html index 42bc8211..c894f4fb 100644 --- a/BTPanel/templates/default/firewall.html +++ b/BTPanel/templates/default/firewall.html @@ -61,16 +61,22 @@ font-family: Courier New; font-size: 12px; } - .security_detail pre { - white-space: pre-wrap; - white-space: -moz-pre-wrap; - white-space: -pre-wrap; - white-space: -o-pre-wrap; - word-wrap: break-word; - } - .detail_tips { - margin-top: 15px; - } + + .security_detail pre { + white-space: pre-wrap; + white-space: -moz-pre-wrap; + white-space: -pre-wrap; + white-space: -o-pre-wrap; + word-wrap: break-word; + } + + .detail_tips { + margin-top: 15px; + } + + .setchmod { + padding-top: 0; + }
                                      @@ -80,7 +86,7 @@
                                      -
                                      +
                                      {{data['lan']['BTN1']}} @@ -90,7 +96,7 @@
                                      -
                                      +
                                      {{data['lan']['BTN2']}} @@ -160,8 +166,8 @@
                                      -
                                      -
                                      +
                                      +
                                      @@ -171,16 +177,17 @@ {% endblock %} {% block scripts %} +{{ super() }} {% endblock %} diff --git a/BTPanel/templates/default/index.html b/BTPanel/templates/default/index.html index 6ad3774e..5716df75 100644 --- a/BTPanel/templates/default/index.html +++ b/BTPanel/templates/default/index.html @@ -69,28 +69,31 @@ }
                                      -
                                      -
                                      - - {{data['lan']['ACCOUNT']}} - - {% if data['pd'].find("{{data['lan']['ACCOUNT']}}") != -1 %} - - {{data['lan']['INVITATION_REWARD']}} - - {% endif %} - - {{data['lan']['S1']}} {{data['lan']['S2']}}  {{data['lan']['S3']}} {{data['lan']['S4']}} -
                                      - - {{data['pd']|safe}} - {{session['version']}} - {{data['lan']['UPDATE']}} - {{data['lan']['FIX']}} - {{data['lan']['RESTART']}} - -
                                      +
                                      +
                                      + + {{data['lan']['ACCOUNT']}} + + {% if data['pd'].find("{{data['lan']['ACCOUNT']}}") != -1 %} + + {{data['lan']['INVITATION_REWARD']}} + + {% endif %} + + {{data['lan']['S1']}} {{data['lan']['S2']}}  {{data['lan']['S3']}} {{data['lan']['S4']}} +
                                      + + {{data['pd']|safe}} + + {{session['version']}} + + {{data['lan']['UPDATE']}} + {{data['lan']['FIX']}} + {{data['lan']['RESTART']}} + +
                                      +
                                      @@ -249,7 +252,7 @@ {% block scripts %} - +{{ super() }} diff --git a/BTPanel/templates/default/layout.html b/BTPanel/templates/default/layout.html index 592723cc..e2d05982 100644 --- a/BTPanel/templates/default/layout.html +++ b/BTPanel/templates/default/layout.html @@ -1,141 +1,152 @@ - - - - - - {{g.title}} - - - - - - + + + + + {{g.title}} + + + + + {% for css_f in g['other_css'] %} + + {% endfor %} + + - -
                                      -
                                      The current version of IE browser is too low, some functions cannot be displayed, please change to other browsers!
                                      - - -
                                      - - - {% block content %}{% endblock %} - +
                                      + + +
                                      + + + {% block content %}{% endblock %} + +
                                      + + + {% block scripts %} + - - + + + @@ -146,134 +157,126 @@ {% for js_f in g['other_js'] %} {% endfor %} - {% block scripts %}{% endblock %} - -
                                      + my_init += 1 + setTimeout(function() { + task_stat(my_init); + }, 1000); + }); + } + function show_task() { + task_open = layer.open({ + type: 1, + title: "Real time task queue", + area: '500px', + closeBtn: 2, + shadeClose: false, + shade: false, + offset: 'auto', + content: '
                                      ', + cancel: function() { + task_close = true; + } + }); + } + function remove_task(id) { + layer.confirm('Do you want to cancel the current task queue?', { + title: 'Cancel task queue', + icon: 0 + }, function (indexs) { + layer.close(indexs); + var loadT = bt.load('Canceling task...'); + $.post('/task?action=remove_task', { + id: id + }, function(rdata) { + loadT.close() + bt.msg(rdata); + }); + }); + } + loadScript([ + '{{g.cdn_url}}/laydate/laydate.js', + '{{g.cdn_url}}/js/jquery.qrcode.min.js', + '{{g.cdn_url}}/js/clipboard.min.js' + ], function(e) {}); + task_stat(); + + {% endblock %} +
                                      \ No newline at end of file diff --git a/BTPanel/templates/default/login.html b/BTPanel/templates/default/login.html index 1c207fd1..5a0ced94 100644 --- a/BTPanel/templates/default/login.html +++ b/BTPanel/templates/default/login.html @@ -1,323 +1,1047 @@ - + - - - - - - - {{g.title}} - - - - - - -
                                      - -
                                      - - - - - - - + }()); + if (ie_version != -1 && ie_version < 9) window.location.href = '/tips' + + + +
                                      +{# #} +
                                      + +
                                      +
                                      {{data['lan']['QR_CORE_LOGIN']}}
                                      +
                                      +
                                      + +
                                      +
                                      +
                                      Scan
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      {{data['lan']['SCAN_MORE_SAFETY']}}
                                      +
                                      +
                                      +
                                      +
                                      + + + - \ No newline at end of file diff --git a/BTPanel/templates/default/site.html b/BTPanel/templates/default/site.html index 53295e21..93fa873f 100644 --- a/BTPanel/templates/default/site.html +++ b/BTPanel/templates/default/site.html @@ -13,7 +13,7 @@
                                      -
                                      + +
                                      +
                                      +
                                      PHP Project
                                      +
                                      Node Project
                                      +
                                      -
                                      -
                                      +
                                      + +
                                      -
                                      +
                                      +
                                      +
                                      -
                                      +
                                      +
                                      +
                                      @@ -50,6 +60,7 @@ {% block scripts %} +{{ super() }} diff --git a/BTPanel/templates/default/tips.html b/BTPanel/templates/default/tips.html index 5a04616b..00b9e6cf 100644 --- a/BTPanel/templates/default/tips.html +++ b/BTPanel/templates/default/tips.html @@ -1,4 +1,4 @@ - + diff --git a/BTPanel/templates/default/xterm.html b/BTPanel/templates/default/xterm.html index f10b3be3..26f3b73c 100644 --- a/BTPanel/templates/default/xterm.html +++ b/BTPanel/templates/default/xterm.html @@ -63,19 +63,29 @@
                                      Password
                                      - +
                                      Private key
                                      - + +
                                      +
                                      +
                                      + Key password +
                                      +
                                      Remarks
                                      - +
                                      @@ -91,13 +101,16 @@
                                      Content
                                      - +
                                      {% endblock %} {% block scripts %} +{{ super() }} diff --git a/class/acme_v2.py b/class/acme_v2.py index 9f022ec7..55895998 100644 --- a/class/acme_v2.py +++ b/class/acme_v2.py @@ -1,5 +1,5 @@ #!/usr/bin/python -#coding: utf-8 +# coding: utf-8 # ------------------------------------------------------------------- # 宝塔Linux面板 # ------------------------------------------------------------------- @@ -18,21 +18,22 @@ import binascii import hashlib import base64 import json -import copy import time import os import sys + os.chdir('/www/server/panel') if not 'class/' in sys.path: - sys.path.insert(0,'class/') + sys.path.insert(0, 'class/') import http_requests as requests + requests.DEFAULT_TYPE = 'curl' import public try: import OpenSSL except: - public.ExecShell("pip install -I pyopenssl") + public.ExecShell("btpip install -I pyOpenSSL") import OpenSSL try: import dns.resolver @@ -40,6 +41,7 @@ except: public.ExecShell("pip install dnspython") import dns.resolver + class acme_v2: _url = None _apis = None @@ -89,7 +91,7 @@ class acme_v2: result = res.json() if "type" in result: if result['type'] == 'urn:acme:error:serverInternal': - raise Exception(public.getMsg('ACME_MSG_ERR')) + raise Exception(public.get_msg_gettext('Service shutdown or internal error due to maintenance, check [ https://letsencrypt.status.io ] see for more details.')) if not os.path.exists('/www/server/panel/data/http_type.pl'): public.writeFile('/www/server/panel/data/http_type.pl','python') self.get_apis() @@ -125,19 +127,19 @@ class acme_v2: self.set_crond() return account except Exception as ex: - return public.returnMsg(False,str(ex)) + return public.return_msg_gettext(False,str(ex)) # 设置帐户信息 def set_account_info(self, args): if not 'account' in self._config: - return public.returnMsg(False, 'ACME_ACCOUNT_ERR') + return public.return_msg_gettext(False, 'The specified account does not exist') account = json.loads(args.account) if 'email' in account: self._config['email'] = account['email'] del(account['email']) self._config['account'][self._mod_index[self._debug]] = account self.save_config() - return public.returnMsg(True, 'ACME_SUCCESS_ACCOUNT_SETUP') + return public.return_msg_gettext(True, 'Setup successfully!') # 获取订单列表 def get_orders(self, args): @@ -153,19 +155,19 @@ class acme_v2: # 删除订单 def remove_order(self, args): if not 'orders' in self._config: - return public.returnMsg(False, 'ACME_ORDER_NOT_EXIST') + return public.return_msg_gettext(False, 'The specified order does not exist!') if not args.index in self._config['orders']: - return public.returnMsg(False, 'ACME_ORDER_NOT_EXIST') + return public.return_msg_gettext(False, 'The specified order does not exist!') del(self._config['orders'][args.index]) self.save_config() - return public.returnMsg(True, 'ACME_DEL_ODER_SUCCESS') + return public.return_msg_gettext(True, 'Order deleted successfully!') # 取指定订单数据 def get_order_find(self, args): if not 'orders' in self._config: - return public.returnMsg(False, 'ACME_ORDER_NOT_EXIST') + return public.return_msg_gettext(False, 'The specified order does not exist!') if not args.index in self._config['orders']: - return public.returnMsg(False, 'ACME_ORDER_NOT_EXIST') + return public.return_msg_gettext(False, 'The specified order does not exist!') result = self._config['orders'][args.index] result['cert'] = self.get_cert_info(args.index) return result @@ -186,27 +188,27 @@ class acme_v2: if not os.path.exists(path): # 尝试重新下载证书 self.download_cert(args.index) if not os.path.exists(path): - return public.returnMsg(False, 'ACME_GET_CERT_ERR') + return public.return_msg_gettext(False, 'Certificate read failed, directory does not exist!') import panelTask bt_task = panelTask.bt_task() zip_file = path+'/cert.zip' result = bt_task._zip(path, '.', path+'/cert.zip', '/dev/null', 'zip') if not os.path.exists(zip_file): return result - return public.returnMsg(True, zip_file) + return public.return_msg_gettext(True, zip_file) # 吊销证书 def revoke_order(self, index): if type(index) != str: index = index.index if not index in self._config['orders']: - raise Exception(public.getMsg('ACME_ORDER_NOT_EXIST')) + raise Exception(public.get_msg_gettext('The specified order does not exist!')) cert_path = self._config['orders'][index]['save_path'] if not os.path.exists(cert_path): - raise Exception(public.getMsg('ACME_CERT_ERR')) + raise Exception(public.get_msg_gettext('No certificate found for the specified order!')) cert = self.dump_der(cert_path) if not cert: - raise Exception(public.getMsg('ACME_CERT_READ_ERR')) + raise Exception(public.get_msg_gettext('Certificate read failed!')) payload = { "certificate": self.calculate_safe_base64(cert), "reason": 4 @@ -217,7 +219,7 @@ class acme_v2: public.ExecShell("rm -rf {}".format(cert_path)) del(self._config['orders'][index]) self.save_config() - return public.returnMsg(True, "Certificate revoked!") + return public.return_msg_gettext(True, "Certificate revoked!") return res.json() # 取根域名和记录值 @@ -320,7 +322,7 @@ class acme_v2: def create_order(self, domains, auth_type, auth_to, index=None): domains = self.format_domains(domains) if not domains: - raise Exception(public.getMsg('ACME_DOMAIN_ERR')) + raise Exception(public.get_msg_gettext('Need at least a domain name!')) # 构造标识 identifiers = [] for domain_name in domains: @@ -348,7 +350,7 @@ class acme_v2: a_auth = res.json() ret_title = self.get_error(str(a_auth)) raise StopIteration( - "{0} >>>> {1}".format( + "{} >>>> {}".format( ret_title, json.dumps(a_auth) ) @@ -365,7 +367,7 @@ class acme_v2: # 获取验证信息 def get_auths(self, index): if not index in self._config['orders']: - raise Exception(public.getMsg('ACME_ORDER_NOT_EXIST')) + raise Exception(public.get_msg_gettext('The specified order does not exist!')) # 检查是否已经获取过授权信息 if 'auths' in self._config['orders'][index]: @@ -447,7 +449,7 @@ class acme_v2: if not self._config['orders'][index]['auth_type'] in ['http','tls']: return True acme_path = '{}/.well-known/acme-challenge'.format(self._config['orders'][index]['auth_to']) - write_log(public.getMsg('ACME_V_DIR',(acme_path,))) + write_log(public.get_msg_gettext('|-Verify the dir:{}',(acme_path,))) if os.path.exists(acme_path): public.ExecShell("rm -f {}/*".format(acme_path)) acme_path = '/www/server/stop/.well-known/acme-challenge' @@ -476,7 +478,7 @@ class acme_v2: except: err = public.get_error_info() print(err) - raise Exception(public.getMsg('ACME_WRITE_V_FILE_ERR',(err,))) + raise Exception(public.get_msg_gettext('Writing verification file failed: {}',(err,))) # 解析域名 def create_dns_record(self, auth_to, domain, dns_value): @@ -507,7 +509,7 @@ class acme_v2: key = dc['data'][0]['value'] secret = dc['data'][1]['value'] except: - raise Exception(public.getMsg('ACME_DNS_API_ERR')) + raise Exception(public.get_msg_gettext('No valid DNSAPI key information found')) else: key = tmp[1] secret = tmp[2] @@ -526,7 +528,7 @@ class acme_v2: # 验证域名 def auth_domain(self, index): if not index in self._config['orders']: - raise Exception(public.getMsg('ACME_ORDER_NOT_EXIST')) + raise Exception(public.get_msg_gettext('The specified order does not exist!')) # 开始验证 for auth in self._config['orders'][index]['auths']: @@ -553,7 +555,7 @@ class acme_v2: number_of_checks = 0 while True: if desired_status == ['valid', 'invalid']: - write_log(public.getMsg('ACME_QUERY_V_RESULT',(str(number_of_checks + 1),))) + write_log(public.get_msg_gettext('|-{} Query verification results..',(str(number_of_checks + 1),))) time.sleep(self._wait_time) check_authorization_status_response = self.acme_request(url, "") a_auth = check_authorization_status_response.json() @@ -561,7 +563,7 @@ class acme_v2: number_of_checks += 1 if authorization_status in desired_status: if authorization_status == "invalid": - write_log("|-"+public.getMsg('VERIFICATION_FAILED')) + write_log("|-"+public.get_msg_gettext('Verification failed')) try: if 'error' in a_auth['challenges'][0]: ret_title = a_auth['challenges'][0]['error']['detail'] @@ -575,7 +577,7 @@ class acme_v2: except: ret_title = str(a_auth) raise StopIteration( - "{0} >>>> {1}".format( + "{} >>>> {}".format( ret_title, json.dumps(a_auth) ) @@ -584,75 +586,75 @@ class acme_v2: if number_of_checks == self._max_check_num: raise StopIteration( - public.getMsg('ACME_V_TIMES',( + public.get_msg_gettext('Error: Attempted verification {} times. The maximum number of verifications is {}. The verification interval is {} seconds.',( str(number_of_checks), str(self._max_check_num), str(self._wait_time) ))) if desired_status == ['valid', 'invalid']: - write_log(public.getMsg('ACME_V_SUCCESS')) + write_log(public.get_msg_gettext('|-Verification succeeded!')) return check_authorization_status_response # 格式化错误输出 def get_error(self, error): if error.find("Max checks allowed") >= 0: - return public.getMsg('ACME_ERR_MSG1') + return public.get_msg_gettext('CA cannot 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 or error.find('status_code=0 ') != -1: - return public.getMsg('ACME_ERR_MSG2') + return public.get_msg_gettext('CA server connection timed out, please try again later.') elif error.find("The domain name belongs") >= 0: - return public.getMsg('ACME_ERR_MSG3') + return public.get_msg_gettext('The domain name does not belong to this DNS service provider, please make sure the domain name is filled in correctly.') elif error.find('login token ID is invalid') >= 0: - return public.getMsg('ACME_ERR_MSG4') + return public.get_msg_gettext('DNS server connection failed, please check if the key is correct.') elif error.find('Error getting validation data') != -1: - return public.getMsg('ACME_ERR_MSG5') + return public.get_msg_gettext('Data validation failed and the CA was unable to get the correct captcha from the authenticated connection.') elif "too many certificates already issued for exact set of domains" in error: - return public.getMsg('ACME_ERR_MSG6',(str(re.findall("exact set of domains: (.+):", error)),)) + return public.get_msg_gettext('Issuing failed, the domain {} has exceeded the limit of weekly reissues!',(str(re.findall("exact set of domains: (.+):", error)),)) elif "Error creating new account :: too many registrations for this IP" in error: - return public.getMsg('ACME_ERR_MSG7') + return public.get_msg_gettext('Issuing failed, the current server IP has reached the limit of creating up to 10 accounts every 3 hours.') elif "DNS problem: NXDOMAIN looking up A for" in error: - return public.getMsg('ACME_ERR_MSG8') + return public.get_msg_gettext('Validation failed, domain name was not resolved, or resolution did not take effect!') elif "Invalid response from" in error: - return public.getMsg('ACME_ERR_MSG9') + return public.get_msg_gettext('Verification failed, domain name resolution error or verification URL cannot be accessed!') elif error.find('TLS Web Server Authentication') != -1: - return public.getMsg('ACME_ERR_MSG10') + return public.get_msg_gettext('Connection to CA server failed, please try again later.') elif error.find('Name does not end in a public suffix') != -1: - return public.getMsg('ACME_ERR_MSG11',(str(re.findall("Cannot issue for \"(.+)\":", error)),)) + return public.get_msg_gettext('Unsupported domain name {}, please check the domain name is correct!',(str(re.findall("Cannot issue for \"(.+)\":", error)),)) elif error.find('No valid IP addresses found for') != -1: - return public.getMsg('ACME_ERR_MSG12',(str(re.findall("No valid IP addresses found for (.+)", error)),)) + return public.get_msg_gettext('No resolution record was found for domain name {}, please check if the domain name resolution takes effect!',(str(re.findall("No valid IP addresses found for (.+)", error)),)) elif error.find('No TXT record found at') != -1: - return public.getMsg('ACME_ERR_MSG13',(str(re.findall("No TXT record found at (.+)", error)),)) + return public.get_msg_gettext('No valid TXT resolution record was found in the domain name {}, please check whether the TXT record is parsed correctly. If it is applied by DNSAPI, please try again in 10 minutes!',(str(re.findall("No TXT record found at (.+)", error)),)) elif error.find('Incorrect TXT record') != -1: - return public.getMsg('ACME_ERR_MSG14',(str(re.findall("found at (.+)", error)), str(re.findall("Incorrect TXT record \"(.+)\"", error)))) + return public.get_msg_gettext('A wrong TXT record was found on {}: {}, please check whether the TXT resolution is correct, if it is applied by DNSAPI, please try again in 10 minutes!',(str(re.findall("found at (.+)", error)), str(re.findall("Incorrect TXT record \"(.+)\"", error)))) elif error.find('Domain not under you or your user') != -1: - return public.getMsg('ACME_ERR_MSG15') + return public.get_msg_gettext('This domain name does not exist under this dnspod account, adding resolution failed!') elif error.find('SERVFAIL looking up TXT for') != -1: - return public.getMsg('ACME_ERR_MSG16',(str(re.findall("looking up TXT for (.+)", error)),)) + return public.get_msg_gettext('No valid TXT resolution record was found in the domain name {}, please check whether the TXT record is parsed correctly. If it is applied by DNSAPI, please try again in 10 minutes!',(str(re.findall("looking up TXT for (.+)", error)),)) elif error.find('Timeout during connect') != -1: - return public.getMsg('ACME_ERR_MSG17') + return public.get_msg_gettext('The connection timed out and the CA server was unable to access your website!') elif error.find("DNS problem: SERVFAIL looking up CAA for") != -1: - return public.getMsg('ACME_ERR_MSG18',(str(re.findall("looking up CAA for (.+)", error)),)) + return public.get_msg_gettext('Domain name {} is currently required to verify the CAA record, please parse the CAA record manually, or retry the application after 1 hour!',(str(re.findall("looking up CAA for (.+)", error)),)) elif error.find("Read timed out.") != -1: - return public.getMsg('ACME_ERR_MSG19') + return public.get_msg_gettext('The verification timed out. Please check if the domain name is resolved correctly. If it is resolved correctly, the connection between the server and LetsEncrypt may be abnormal. Please try again later!') elif error.find('Cannot issue for') != -1: - return public.getMsg('ACME_ERR_MSG20',(str(re.findall(r'for\s+"(.+)"',error)),)) + return public.get_msg_gettext('Cannot issue a certificate for {}, cannot apply for a wildcard certificate with a domain name suffix directly!',(str(re.findall(r'for\s+"(.+)"',error)),)) elif error.find('too many failed authorizations recently'): - return public.getMsg('ACME_ERR_MSG21') + return public.get_msg_gettext('The account has more than 5 failed orders within 1 hour, please wait 1 hour and try again!') elif error.find("Error creating new order") != -1: - return public.getMsg('ACME_ERR_MSG22') + return public.get_msg_gettext('Order creation failed, please try again later!') elif error.find("Too Many Requests") != -1: - return public.getMsg('ACME_ERR_MSG23') + return public.get_msg_gettext('More than 5 verification failures in 1 hour, the application is temporarily banned, please try again later!') elif error.find('HTTP Error 400: Bad Request') != -1: - return public.getMsg('ACME_ERR_MSG24') + return public.get_msg_gettext('CA server denied access, please try again later!') elif error.find('Temporary failure in name resolution') != -1: - return public.getMsg('ACME_ERR_MSG25') + return public.get_msg_gettext('The DNS of the server is faulty and the domain name cannot be resolved. Please use the Linux toolbox to check the DNS configuration') elif error.find('Too Many Requests') != -1: - return public.getMsg('ACME_ERR_MSG26') + return public.get_msg_gettext('Too many requests for this domain name. Please try again 3 hours later') else: return error # 发送验证请求 def respond_to_challenge(self, auth): - payload = {"keyAuthorization": "{0}".format( + payload = {"keyAuthorization": "{}".format( auth['acme_keyauthorization'])} respond_to_challenge_response = self.acme_request( auth['dns_challenge_url'], payload) @@ -666,7 +668,7 @@ class acme_v2: url=self._config['orders'][index]['finalize'], payload=payload) if send_csr_response.status_code not in [200, 201]: raise ValueError( - public.getMsg('ACME_SEND_CSR_ERR',(send_csr_response.status_code,send_csr_response.json())) + public.get_msg_gettext('Error: Sending CSR: Response status {} Response value: {}',(send_csr_response.status_code,send_csr_response.json())) ) send_csr_response_json = send_csr_response.json() certificate_url = send_csr_response_json["certificate"] @@ -689,7 +691,7 @@ class acme_v2: res = self.acme_request( self._config['orders'][index]['certificate_url'], "") if res.status_code not in [200, 201]: - raise Exception(public.getMsg('ACME_CERT_DOWNLOAD_ERR',(str(res.json()),))) + raise Exception(public.get_msg_gettext('Failed to download certificate: {}',(str(res.json()),))) pem_certificate = res.content if type(pem_certificate) == bytes: @@ -781,8 +783,10 @@ fullchain.pem Paste into certificate input box # 获取目标证书的基本信息 to_cert_init = self.get_cert_init(to_pem_file) # 判断证书品牌是否一致 - if to_cert_init['issuer'] != cert_init['issuer'] and to_cert_init['issuer'].find("Let's Encrypt") == -1: - continue + try: + if to_cert_init['issuer'] != cert_init['issuer'] and to_cert_init['issuer'].find("Let's Encrypt") == -1 and to_cert_init['issuer'] != 'R3': + continue + except: continue # 判断目标证书的到期时间是否较早 if to_cert_init['notAfter'] > cert_init['notAfter']: continue @@ -802,7 +806,7 @@ fullchain.pem Paste into certificate input box public.writeFile( to_key_file, public.readFile(key_file, 'rb'), 'wb') public.writeFile(to_info, json.dumps(cert_init)) - write_log(public.getMsg('ACME_CERT_REPLACE',(to_path,))) + write_log(public.get_msg_gettext('|-Detected that the certificate under {} overlaps with the certificate of this application and has an earlier expiration time, and has been replaced with a new certificate!',(to_path,))) # 重载web服务 public.serviceReload() if is_panel: public.restart_panel() @@ -818,7 +822,7 @@ fullchain.pem Paste into certificate input box for domain in self._config['orders'][index]['domains']: if domain in cert_init['dns']: return index - if cert_init['issuer'].find("Let's Encrypt") != -1: + if cert_init['issuer'].find("Let's Encrypt") != -1 or cert_init['issuer'] == 'R3': return pem_file return None except: return None @@ -828,10 +832,10 @@ fullchain.pem Paste into certificate input box if not os.path.exists(args.pem_file): args.pem_file = 'vhost/cert/{}/fullchain.pem'.format(args.siteName) if not os.path.exists(args.pem_file): - return public.returnMsg(False, 'ACME_CERT_FILE_ERR') + return public.return_msg_gettext(False, 'The specified certificate file does not exist!') cert_init = self.get_cert_init(args.pem_file) if not cert_init: - return public.returnMsg(False, 'ACME_CERT_GET_CERTINFO_ERR') + return public.return_msg_gettext(False, 'Certificate information acquisition failed!') cert_init['dnsapi'] = json.loads(public.readFile(self._dnsapi_file)) return cert_init @@ -922,7 +926,7 @@ fullchain.pem Paste into certificate input box # 检查DNS记录 def check_dns(self, domain, value, s_type='TXT'): - write_log(public.getMsg('ACME_CHECK_DNS',(domain, s_type, value))) + write_log(public.get_msg_gettext('|-Attempt to verify DNS records locally, domain name: {}, type: {} record value: {}',(domain, s_type, value))) time.sleep(10) n = 0 while n < 20: @@ -933,9 +937,9 @@ fullchain.pem Paste into certificate input box for j in ns.response.answer: for i in j.items: txt_value = i.to_text().replace('"', '').strip() - write_log(public.getMsg('ACME_CHECK_DNS1',(str(n),txt_value))) + write_log(public.get_msg_gettext('|-Number of verifications: {}, value: {}',(str(n),txt_value))) if txt_value == value: - write_log(public.getMsg('ACME_CHECK_DNS2')) + write_log(public.get_msg_gettext('|-Local authentication succeeded!')) return True except: try: @@ -943,7 +947,7 @@ fullchain.pem Paste into certificate input box except: return False time.sleep(3) - write_log(public.getMsg('ACME_CHECK_DNS3')) + write_log(public.get_msg_gettext('|-Local authentication failed!')) return True # 创建CSR @@ -954,11 +958,11 @@ fullchain.pem Paste into certificate input box X509Req = OpenSSL.crypto.X509Req() X509Req.get_subject().CN = domain_name if domain_alt_names: - SAN = "DNS:{0}, ".format(domain_name).encode("utf8") + ", ".join( + SAN = "DNS:{}, ".format(domain_name).encode("utf8") + ", ".join( "DNS:" + i for i in domain_alt_names ).encode("utf8") else: - SAN = "DNS:{0}".format(domain_name).encode("utf8") + SAN = "DNS:{}".format(domain_name).encode("utf8") X509Req.add_extensions( [ @@ -984,7 +988,7 @@ fullchain.pem Paste into certificate input box acme_thumbprint = self.calculate_safe_base64( hashlib.sha256(acme_header_jwk_json.encode("utf8")).digest() ) - acme_keyauthorization = "{0}.{1}".format(token, acme_thumbprint) + acme_keyauthorization = "{}.{}".format(token, acme_thumbprint) base64_of_acme_keyauthorization = self.calculate_safe_base64( hashlib.sha256(acme_keyauthorization.encode("utf8")).digest() ) @@ -994,7 +998,7 @@ fullchain.pem Paste into certificate input box # 构造验证信息 def get_identifier_auth(self, index, url, auth_info): s_type = self.get_auth_type(index) - write_log(public.getMsg('ACME_BUILD_AUTH',(s_type,))) + write_log(public.get_msg_gettext('|-Verification type: {}',(s_type,))) domain = auth_info['identifier']['value'] wildcard = False # 处理通配符 @@ -1019,7 +1023,7 @@ fullchain.pem Paste into certificate input box # 获取域名验证方式 def get_auth_type(self, index): if not index in self._config['orders']: - raise Exception(public.getMsg('ACME_ORDER_NOT_EXIST')) + raise Exception(public.get_msg_gettext('The specified order does not exist!')) s_type = 'http-01' if 'auth_type' in self._config['orders'][index]: if self._config['orders'][index]['auth_type'] == 'dns': @@ -1087,7 +1091,7 @@ fullchain.pem Paste into certificate input box elif self._config['email']: payload = { "termsOfServiceAgreed": True, - "contact": ["mailto:{0}".format(self._config['email'])], + "contact": ["mailto:{}".format(self._config['email'])], } else: payload = {"termsOfServiceAgreed": True} @@ -1095,7 +1099,7 @@ fullchain.pem Paste into certificate input box res = self.acme_request(url=self._apis['newAccount'], payload=payload) if res.status_code not in [201, 200, 409]: - raise Exception(public.getMsg('ACME_REGISTERED_ERR',(str(res.json()),))) + raise Exception(public.get_msg_gettext('Registration for ACME account failed: {}',(str(res.json()),))) kid = res.headers["Location"] return kid @@ -1111,7 +1115,7 @@ fullchain.pem Paste into certificate input box protected = self.get_acme_header(url) protected64 = self.calculate_safe_base64(json.dumps(protected)) signature = self.sign_message( - message="{0}.{1}".format(protected64, payload64)) # bytes + message="{}.{}".format(protected64, payload64)) # bytes signature64 = self.calculate_safe_base64(signature) # str data = json.dumps( {"protected": protected64, "payload": payload64, @@ -1172,7 +1176,7 @@ fullchain.pem Paste into certificate input box public_key_public_numbers = private_key.public_key().public_numbers() exponent = "{0:x}".format(public_key_public_numbers.e) - exponent = "0{0}".format(exponent) if len( + exponent = "0{}".format(exponent) if len( exponent) % 2 else exponent modulus = "{0:x}".format(public_key_public_numbers.n) jwk = { @@ -1268,23 +1272,25 @@ fullchain.pem Paste into certificate input box index = None if 'index' in args: index = args['index'] + if 'auto_wildcard' in args: + self._auto_wildcard = 1 if not index: # 判断是否只想验证域名 - write_log(public.getMsg('ACME_CREAT_ORDER')) + write_log(public.get_msg_gettext('|-Creating order..')) index = self.create_order(domains, auth_type, auth_to) - write_log(public.getMsg('ACME_GET_V')) + write_log(public.get_msg_gettext('|-Getting verification information..')) self.get_auths(index) if auth_to == 'dns' and len(self._config['orders'][index]['auths']) > 0: return self._config['orders'][index] - write_log(public.getMsg('ACME_V_DOMAIN')) + write_log(public.get_msg_gettext('|-Verifying domain name..')) self.auth_domain(index) self.remove_dns_record() - write_log(public.getMsg('ACME_SEND_CSR')) + write_log(public.get_msg_gettext('|-Sending CSR..')) self.send_csr(index) - write_log(public.getMsg('ACME_DOWNLOAD_CERT')) + write_log(public.get_msg_gettext('|-Downloading certificate..')) cert = self.download_cert(index) cert['status'] = True - cert['msg'] = public.getMsg('ACME_APPLY_SUCCESS') - write_log(public.getMsg('ACME_APPLY_SUCCESS1')) + cert['msg'] = public.get_msg_gettext('Application successful!') + write_log(public.get_msg_gettext('|-Successful application, deploying to site..')) return cert except Exception as ex: self.remove_dns_record() @@ -1295,7 +1301,7 @@ fullchain.pem Paste into certificate input box else: msg = ex write_log(public.get_error_info()) - return public.returnMsg(False, msg) + return public.return_msg_gettext(False, msg) # 申请证书 - api def apply_cert_api(self, args): @@ -1307,7 +1313,7 @@ fullchain.pem Paste into certificate input box try: project_info = json.loads(project_info) if not 'ssl_path' in project_info: - return public.returnMsg(False, 'There is a problem with the current Java project configuration file, please rebuild') + return public.return_msg_gettext(False, 'There is a problem with the current Java project configuration file, please rebuild') if not os.path.exists(project_info['ssl_path']): os.makedirs(project_info['ssl_path']) path = project_info['ssl_path'] @@ -1319,7 +1325,7 @@ fullchain.pem Paste into certificate input box self._auto_wildcard = True return self.apply_cert(json.loads(args.domains), args.auth_type, args.auth_to) except: - return public.returnMsg(False, 'There is a problem with the current Java project configuration file, please rebuild') + return public.return_msg_gettext(False, 'There is a problem with the current Java project configuration file, please rebuild') else: if re.match(r"^\d+$", args.auth_to): import panelSite @@ -1330,7 +1336,7 @@ fullchain.pem Paste into certificate input box args.auth_to = args.auth_to[:-1] if not os.path.exists(args.auth_to): - return public.returnMsg(False, 'ACME_DIR_ERR') + return public.return_msg_gettext(False, 'Invalid site directory, please check if the specified site exists!') check_result = self.check_auth_env(args, check=True) if check_result: return check_result @@ -1423,8 +1429,8 @@ fullchain.pem Paste into certificate input box return for domain in json.loads(args.domains): if public.checkIp(domain): continue - if domain.find('*.') >=0 and args.auth_type in ['http','tls']: - raise public.returnMsg(False, 'ACME_PAN_DOMAIN_ERR') + if domain.find('*.') != -1 and args.auth_type in ['http','tls']: + raise public.return_msg_gettext(False, 'Pan domain names cannot apply for a certificate using [File Verification]!') import panelSite s = panelSite.panelSite() if args.auth_type in ['http','tls']: @@ -1460,7 +1466,7 @@ fullchain.pem Paste into certificate input box s.ModifyRedirect(args) redirect_tmp[args.sitename].append(x['redirectname']) else: - if x['type']: return public.returnMsg(False, 'SITE_SSL_ERR_301') + if x['type']: return public.return_msg_gettext(False, 'Your site has 301 Redirect on,Please turn it off first!') if redirect_tmp[args.sitename]: public.writeFile('{}/data/stop_r_tmp.pl'.format(public.get_panel_path()),json.dumps(redirect_tmp)) data = s.GetProxyList(args) @@ -1486,13 +1492,13 @@ fullchain.pem Paste into certificate input box write_log("|- Turning off proxy {}".format(args.proxyname)) proxy_tmp[args.sitename].append(x['proxyname']) else: - if x['type']: return public.returnMsg(False,'ACME_PROXY_ERR') + if x['type']: return public.return_msg_gettext(False,'Sites with reverse proxy turned on cannot apply for SSL!') if proxy_tmp[args.sitename]: public.writeFile('{}/data/stop_p_tmp.pl'.format(public.get_panel_path()),json.dumps(proxy_tmp)) # 检查旧重定向是否开启 data = s.Get301Status(args) if data['status']: - return public.returnMsg(False,'SITE_SSL_ERR_3011') + return public.return_msg_gettext(False,'The website has been redirected, please close it before applying!') #判断是否强制HTTPS if s.IsToHttps(args.siteName): if os.path.exists(self._stop_rp_file): @@ -1501,14 +1507,14 @@ fullchain.pem Paste into certificate input box s.CloseToHttps(args) public.writeFile('{}/data/stop_httptohttps.pl'.format(public.get_panel_path()), '') else: - return public.returnMsg(False, 'ACME_FORCE_SSL_ERR') + return public.return_msg_gettext(False, 'After configuring Force HTTPS, you cannot use [File Verification] to apply for a certificate!') public.serviceReload() except: return False else: if args.auth_to.find('Dns_com') != -1: if not os.path.exists('plugin/dns/dns_main.py'): - return public.returnMsg(False, 'ACME_DNS_ERR') + return public.return_msg_gettext(False, 'Please go to the software store to install [cloud analysis], and complete the domain name NS binding.') return False # DNS手动验证 @@ -1601,6 +1607,138 @@ fullchain.pem Paste into certificate input box site_status = public.M('sites').where('id=?', (site_id,)).field('status').select()[0]['status'] return site_status + def get_index(self, domains): + ''' + @name 获取标识 + @author hwliang<2022-02-10> + @param domains 域名列表 + @return string + ''' + identifiers = [] + for domain_name in domains: + identifiers.append({"type": 'dns', "value": domain_name}) + return public.md5(json.dumps(identifiers)) + + # 续签同品牌其它证书 + def renew_cert_other(self): + ''' + @name 续签同品牌其它证书 + @author hwliang<2022-02-10> + @return void + ''' + cert_path = "{}/vhost/cert".format(public.get_panel_path()) + if not os.path.exists(cert_path): return + new_time = time.time() + (86400 * 30) + n = 0 + if not 'orders' in self._config: self._config['orders'] = {} + import panelSite + siteObj = panelSite.panelSite() + args = public.dict_obj() + for siteName in os.listdir(cert_path): + try: + cert_file = '{}/{}/fullchain.pem'.format(cert_path, siteName) + if not os.path.exists(cert_file): continue # 无证书文件 + siteInfo = public.M('sites').where('name=?', siteName).find() + if not siteInfo: continue # 无网站信息 + cert_init = self.get_cert_init(cert_file) + if not cert_init: continue # 无法获取证书 + end_time = time.mktime(time.strptime(cert_init['notAfter'], '%Y-%m-%d')) + if end_time > new_time: continue # 未到期 + try: + if not cert_init['issuer'] in ['R3', "Let's Encrypt"] and cert_init['issuer'].find( + "Let's Encrypt") == -1: + continue # 非同品牌证书 + except: + continue + + if isinstance(cert_init['dns'], str): cert_init['dns'] = [cert_init['dns']] + index = self.get_index(cert_init['dns']) + if index in self._config['orders'].keys(): continue # 已在订单列表 + + n += 1 + write_log("|-Renewing additional certificate {}, domain name:{}..".format(n, cert_init['subject'])) + write_log("|-Creating order..") + args.id = siteInfo['id'] + runPath = siteObj.GetRunPath(args) + if runPath and not runPath in ['/']: + path = siteInfo['path'] + '/' + runPath + else: + path = siteInfo['path'] + + self.renew_cert_to(cert_init['dns'],'http',path.replace('//','/')) + except: + write_log("|-Renewal failed:") + + def renew_cert_to(self, domains, auth_type, auth_to, index=None): + siteName = None + cert = {} + args = public.dict_obj() + if auth_to[-1] == "/": + auth_to = auth_to[:-1] + site_id = public.M('sites').where('path=?', auth_to).getField('id') + args.id = site_id + if os.path.exists(auth_to): + if public.M('sites').where('path=?', auth_to).count() == 1: + # site_id = public.M('sites').where('path=?',auth_to).getField('id') + siteName = public.M('sites').where('path=?', auth_to).getField('name') + import panelSite + siteObj = panelSite.panelSite() + # args = public.dict_obj() + # args.id = site_id + runPath = siteObj.GetRunPath(args) + if runPath and not runPath in ['/']: + path = auth_to + '/' + runPath + if os.path.exists(path): auth_to = path.replace('//', '/') + + else: + siteName = self.get_site_name_by_domains(domains) + try: + index = self.create_order( + domains, + auth_type, + auth_to.replace('//', '/'), + index + ) + + write_log("|-Getting verification information..") + self.get_auths(index) + write_log("|-Verifying domain name..") + self.auth_domain(index) + write_log("|-Sending CSR..") + self.remove_dns_record() + self.send_csr(index) + write_log("|-Downloading certificate..") + cert = self.download_cert(index) + self._config['orders'][index]['renew_time'] = int(time.time()) + + # 清理失败重试记录 + self._config['orders'][index]['retry_count'] = 0 + self._config['orders'][index]['next_retry_time'] = 0 + + # 保存证书配置 + self.save_config() + cert['status'] = True + cert['msg'] = 'Renewed successfully!' + write_log("|-Renewed successfully!!") + except Exception as e: + if str(e).find('please try again later') == -1: # 受其它证书影响和连接CA失败的的不记录重试次数 + if index: + # 设置下次重试时间 + self._config['orders'][index]['next_retry_time'] = int(time.time() + (86400 * 2)) + # 记录重试次数 + if not 'retry_count' in self._config['orders'][index].keys(): + self._config['orders'][index]['retry_count'] = 1 + self._config['orders'][index]['retry_count'] += 1 + # 保存证书配置 + self.save_config() + msg = str(e).split('>>>>')[0] + write_log("|-" + msg) + return public.returnMsg(False, msg) + finally: + self.turnon_redirect_proxy_httptohttps(args) + write_log("-" * 70) + return cert + # 续签证书 def renew_cert(self, index): write_log("", "wb+") @@ -1612,10 +1750,11 @@ fullchain.pem Paste into certificate input box # 在面板点击申请证书时不要重启面板以防后续请求出错 self._by_panel = True if index not in self._config['orders']: - raise Exception(public.getMsg('ACME_RENEW_ERR')) + raise Exception(public.get_msg_gettext('The specified order number does not exist and cannot be renewed!')) order_index.append(index) else: s_time = time.time() + (30 * 86400) + if not 'orders' in self._config: self._config['orders'] = {} for i in self._config['orders'].keys(): if not 'save_path' in self._config['orders'][i]: continue @@ -1645,17 +1784,21 @@ fullchain.pem Paste into certificate input box # 是否到了最大重试次数 if 'retry_count' in self._config['orders'][i]: - if self._config['orders'][i]['retry_count'] >= 3: - write_log('|-Skip the domain name this time: {}, this certificate will not be renewed due to failure to renew 3 times in a row, (The number of errors will be reset after manual renewal and success)'.format(self._config['orders'][i]['domains'])) + if self._config['orders'][i]['retry_count'] >= 5: + write_log('|-Skip the domain name this time: {}, this certificate will not be renewed due to failure to renew 5 times in a row, (The number of errors will be reset after manual renewal and success)'.format(self._config['orders'][i]['domains'])) continue # 加入到续签订单 order_index.append(i) if not order_index: - write_log(public.getMsg('ACME_NO_NEED_RENEW')) - return public.returnMsg(False,public.getMsg('ACME_NO_NEED_RENEW')) - write_log(public.getMsg("ACME_NEED_RENEW",(str(len(order_index)),))) + write_log(public.get_msg_gettext('|-No SSL certificate found within 30 days!')) + self.get_apis() + self.renew_cert_other() + # return public.return_msg_gettext(False,public.get_msg_gettext('|-No SSL certificate found within 30 days!')) + write_log("|-All tasks have been processed!") + return + write_log(public.get_msg_gettext('|-A total of {} certificates need to be renewed',(str(len(order_index)),))) n = 0 self.get_apis() cert = None @@ -1671,53 +1814,54 @@ fullchain.pem Paste into certificate input box write_log('|-Renew the visa certificate and start checking the environment') self.check_auth_env(args,check=True) n += 1 - write_log(public.getMsg("ACME_RENEWING",(str(n),str(self._config['orders'][index]['domains'])))) - write_log(public.getMsg('ACME_CREAT_ORDER')) - try: - run_path = self.get_site_runpath(self._config['orders'][index]['domains']) - if run_path: - if self._config['orders'][index]['auth_to'] != run_path: - self._config['orders'][index]['auth_to'] = run_path - index = self.create_order( - self._config['orders'][index]['domains'], - self._config['orders'][index]['auth_type'], - self._config['orders'][index]['auth_to'], - index - ) - write_log(public.getMsg('ACME_GET_V')) - self.get_auths(index) - write_log(public.getMsg('ACME_V_DOMAIN')) - self.auth_domain(index) - write_log(public.getMsg('ACME_SEND_CSR')) - self.remove_dns_record() - self.send_csr(index) - write_log(public.getMsg('ACME_DOWNLOAD_CERT')) - cert = self.download_cert(index) - self._config['orders'][index]['renew_time'] = int(time.time()) - - # 清理失败重试记录 - self._config['orders'][index]['retry_count'] = 0 - self._config['orders'][index]['next_retry_time'] = 0 - - # 保存证书配置 - self.save_config() - cert['status'] = True - cert['msg'] = public.getMsg('ACME_RENEW_SUCCESS') - if os.path.exists(self._stop_rp_file): - self.turnon_redirect_proxy_httptohttps(args) - write_log(public.getMsg('ACME_RENEW_SUCCESS1')) - except Exception as e: - if str(e).find('请稍候重试') == -1: # 受其它证书影响和连接CA失败的的不记录重试次数 - # 设置下次重试时间 - self._config['orders'][index]['next_retry_time'] = int(time.time() + (86400 * 2)) - # 记录重试次数 - if not 'retry_count' in self._config['orders'][index].keys(): - self._config['orders'][index]['retry_count'] = 1 - self._config['orders'][index]['retry_count'] += 1 - # 保存证书配置 - self.save_config() - write_log("|-" + str(e).split('>>>>')[0]) - write_log("-" * 70) + write_log(public.get_msg_gettext('|-Renewing certificate number of {},domain: {}..',(str(n),str(self._config['orders'][index]['domains'])))) + write_log(public.get_msg_gettext('|-Creating order..')) + cert = self.renew_cert_to(self._config['orders'][index]['domains'],self._config['orders'][index]['auth_type'],self._config['orders'][index]['auth_to'],index) + # try: + # run_path = self.get_site_runpath(self._config['orders'][index]['domains']) + # if run_path: + # if self._config['orders'][index]['auth_to'] != run_path: + # self._config['orders'][index]['auth_to'] = run_path + # index = self.create_order( + # self._config['orders'][index]['domains'], + # self._config['orders'][index]['auth_type'], + # self._config['orders'][index]['auth_to'], + # index + # ) + # write_log(public.get_msg_gettext('|-Getting verification information..')) + # self.get_auths(index) + # write_log(public.get_msg_gettext('|-Verifying domain name..')) + # self.auth_domain(index) + # write_log(public.get_msg_gettext('|-Sending CSR..')) + # self.remove_dns_record() + # self.send_csr(index) + # write_log(public.get_msg_gettext('|-Downloading certificate..')) + # cert = self.download_cert(index) + # self._config['orders'][index]['renew_time'] = int(time.time()) + # + # # 清理失败重试记录 + # self._config['orders'][index]['retry_count'] = 0 + # self._config['orders'][index]['next_retry_time'] = 0 + # + # # 保存证书配置 + # self.save_config() + # cert['status'] = True + # cert['msg'] = public.get_msg_gettext('Renewed successfully!') + # if os.path.exists(self._stop_rp_file): + # self.turnon_redirect_proxy_httptohttps(args) + # write_log(public.get_msg_gettext('|-Renewed successfully!')) + # except Exception as e: + # if str(e).find('请稍候重试') == -1: # 受其它证书影响和连接CA失败的的不记录重试次数 + # # 设置下次重试时间 + # self._config['orders'][index]['next_retry_time'] = int(time.time() + (86400 * 2)) + # # 记录重试次数 + # if not 'retry_count' in self._config['orders'][index].keys(): + # self._config['orders'][index]['retry_count'] = 1 + # self._config['orders'][index]['retry_count'] += 1 + # # 保存证书配置 + # self.save_config() + # write_log("|-" + str(e).split('>>>>')[0]) + # write_log("-" * 70) return cert except Exception as ex: self.remove_dns_record() @@ -1728,7 +1872,7 @@ fullchain.pem Paste into certificate input box else: msg = ex write_log(public.get_error_info()) - return public.returnMsg(False, msg) + return public.return_msg_gettext(False, msg) def echo_err(msg): @@ -1751,22 +1895,22 @@ def write_log(log_str, mode="ab+"): if __name__ == "__main__": import argparse - p = argparse.ArgumentParser(usage=public.getMsg('ACME_USE_TIPS')) + p = argparse.ArgumentParser(usage=public.get_msg_gettext('Required parameters: --domain list of domain names, multiple separated by commas!')) p.add_argument('--domain', default=None, - help=public.getMsg('ACME_USE_TIPS1'), dest="domains") - p.add_argument('--type', default=None, help=public.getMsg('ACME_USE_TIPS2'), dest="auth_type") - p.add_argument('--path', default=None, help=public.getMsg('ACME_USE_TIPS3'), dest="path") - p.add_argument('--dnsapi', default=None, help=public.getMsg('ACME_USE_TIPS4'), dest="dnsapi") - p.add_argument('--dns_key', default=None, help=public.getMsg('ACME_USE_TIPS5'), dest="key") - p.add_argument('--dns_secret', default=None,help=public.getMsg('ACME_USE_TIPS6'), dest="secret") - p.add_argument('--index', default=None, help=public.getMsg('ACME_USE_TIPS7'), dest="index") - p.add_argument('--renew', default=None, help=public.getMsg('ACME_USE_TIPS8'), dest="renew") - p.add_argument('--revoke', default=None, help=public.getMsg('ACME_USE_TIPS9'), dest="revoke") + help=public.get_msg_gettext('Please specify the domain name to apply for a certificate'), dest="domains") + p.add_argument('--type', default=None, help=public.get_msg_gettext('Please specify verification type'), dest="auth_type") + p.add_argument('--path', default=None, help=public.get_msg_gettext('Please specify the website document root'), dest="path") + p.add_argument('--dnsapi', default=None, help=public.get_msg_gettext('Please specify DNSAPI'), dest="dnsapi") + p.add_argument('--dns_key', default=None, help=public.get_msg_gettext('Please specify DNSAPI key'), dest="key") + p.add_argument('--dns_secret', default=None,help=public.get_msg_gettext('Please specify DNSAPI secret'), dest="secret") + p.add_argument('--index', default=None, help=public.get_msg_gettext('Specify the order index'), dest="index") + p.add_argument('--renew', default=None, help=public.get_msg_gettext('renew certificate'), dest="renew") + p.add_argument('--revoke', default=None, help=public.get_msg_gettext('Revoke certificate'), dest="revoke") args = p.parse_args() cert = None if args.revoke: if not args.index: - echo_err(public.getMsg('ACME_USE_TIPS10')) + echo_err(public.get_msg_gettext('Please enter the index of the order to be revoked in the --index parameter')) p = acme_v2() result = p.revoke_order(args.index) write_log(result) @@ -1779,24 +1923,24 @@ if __name__ == "__main__": try: if not args.index: if not args.domains: - echo_err(public.getMsg('ACME_USE_TIPS11')) + echo_err(public.get_msg_gettext('Please specify the domain name for which you want to apply for a certificate in the --domain parameter, multiple separated by commas (,)')) if not args.auth_type in ['http', 'tls', 'dns']: - echo_err(public.getMsg('ACME_USE_TIPS12')) + echo_err(public.get_msg_gettext('Please specify the correct authentication type in the --type parameter, supporting dns and http')) auth_to = '' if args.auth_type in ['http', 'tls']: if not args.path: - echo_err(public.getMsg('ACME_USE_TIPS13')) + echo_err(public.get_msg_gettext('Please specify the website document root in the --path parameter!')) if not os.path.exists(args.path): - echo_err(public.getMsg('ACME_USE_TIPS14',(args.path,))) + echo_err(public.get_msg_gettext('The specified site root does not exist, please check: {}',(args.path,))) auth_to = args.path else: if args.dnsapi == '0': auth_to = 'dns' else: if not args.key: - echo_err(public.getMsg('ACME_USE_TIPS15')) + echo_err(public.get_msg_gettext('When applying using dnsapi, specify the dnsapi key in the --dns_key parameter!')) if not args.secret: - echo_err(public.getMsg('ACME_USE_TIPS16')) + echo_err(public.get_msg_gettext('When applying using dnsapi, specify the secret of dnsapi in the --dns_secret parameter!')) auth_to = "{}|{}|{}".format( args.dnsapi, args.key, args.secret) @@ -1808,27 +1952,27 @@ if __name__ == "__main__": acme_txt = '_acme-challenge.' acme_caa = '1 issue letsencrypt.org' write_log("=" * 65) - write_log("\033[32m"+public.getMsg('ACME_USE_TIPS17')+"\033[0m") + write_log("\033[32m"+public.get_msg_gettext('|-Manual order submission is successful, please resolve DNS records according to the following tips: ')+"\033[0m") write_log("=" * 65) - write_log(public.getMsg('ACME_USE_TIPS18',(cert['index'],))) - write_log(public.getMsg('ACME_USE_TIPS19')+": ./acme_v2.py --index=\"{}\"".format(cert['index'])) - write_log(public.getMsg('ACME_USE_TIPS20',(len(cert['auths']),))) + write_log(public.get_msg_gettext('|-Order index: {}',(cert['index'],))) + write_log(public.get_msg_gettext('|-Retry the command')+": ./acme_v2.py --index=\"{}\"".format(cert['index'])) + write_log(public.get_msg_gettext('|-A total of \033[36m{}\033[0m domain name records need to be resolved.',(len(cert['auths']),))) for i in range(len(cert['auths'])): write_log('-' * 70) - write_log(public.getMsg('ACME_USE_TIPS21',(str(i+1), cert['auths'][i]['domain']))) - write_log(public.getMsg('ACME_USE_TIPS22',(acme_txt + cert['auths'][i]['domain'].replace('*.', ''), cert['auths'][i]['auth_value']))) - write_log(public.getMsg('ACME_USE_TIPS23',(cert['auths'][i]['domain'].replace('*.', ''), acme_caa))) + write_log(public.get_msg_gettext('|-The \033[36m{}\033[0m domain names are: {}, please resolve the following information: ',(str(i+1), cert['auths'][i]['domain']))) + write_log(public.get_msg_gettext('|-Record Type: TXT Record Name: \033[41m{}\033[0m Record Value: \033[41m{}\033 [0m [Required]',(acme_txt + cert['auths'][i]['domain'].replace('*.', ''), cert['auths'][i]['auth_value']))) + write_log(public.get_msg_gettext('|-Record type: CAA Record name: \033[41m{}\033[0m Record value: \033[41m{}\033[0m [Optional]',(cert['auths'][i]['domain'].replace('*.', ''), acme_caa))) write_log('-' * 70) input_data = "" while input_data not in ['y', 'Y', 'n', 'N']: - input_msg = public.getMsg('ACME_USE_TIPS24') + input_msg = public.get_msg_gettext('Please wait 2-3 minutes after completing the resolution and enter Y and press Enter to continue verifying the domain name: ') if sys.version_info[0] == 2: input_data = raw_input(input_msg) else: input_data = input(input_msg) if input_data in ['n', 'N']: write_log("=" * 65) - write_log(public.getMsg('ACME_USE_TIPS25')) + write_log(public.get_msg_gettext('|-The user abandons the application and exits the program!')) exit() cert = p.apply_cert( [], auth_type=args.auth_type, auth_to='dns', index=cert['index']) @@ -1843,8 +1987,8 @@ if __name__ == "__main__": if not cert: exit() write_log("=" * 65) - write_log(public.getMsg('ACME_USE_TIPS26')) + write_log(public.get_msg_gettext('|-Certificate obtained successfully!')) write_log("=" * 65) - write_log(public.getMsg('ACME_USE_TIPS27',(','.join(cert['domains']),))) - write_log(public.getMsg('ACME_USE_TIPS28',(public.format_date(times=cert['cert_timeout']),))) - write_log(public.getMsg('ACME_USE_TIPS29',(cert['save_path'],))) + write_log(public.get_msg_gettext('Certified Domain Name: {}',(','.join(cert['domains']),))) + write_log(public.get_msg_gettext('Certificate expiration time: {}',(public.format_date(times=cert['cert_timeout']),))) + write_log(public.get_msg_gettext('Certificate saved at: {}/',(cert['save_path'],))) diff --git a/class/ajax.py b/class/ajax.py index 82a5f94b..36aafcdd 100644 --- a/class/ajax.py +++ b/class/ajax.py @@ -6,7 +6,7 @@ # +------------------------------------------------------------------- # | Author: hwliang # +------------------------------------------------------------------- -from BTPanel import session,request +from BTPanel import session,request,cache import public,os,json,time,apache,psutil class ajax: __official_url = 'https://brandnew.aapanel.com' @@ -25,7 +25,7 @@ class ajax: pass def GetNginxStatus(self,get): try: - if not os.path.exists('/www/server/nginx/sbin/nginx'): return public.returnMsg(False,'NGINX_NOT_INSTALL') + if not os.path.exists('/www/server/nginx/sbin/nginx'): return public.return_msg_gettext(False,'Nginx is not install') process_cpu = {} worker = int(public.ExecShell("ps aux|grep nginx|grep 'worker process'|wc -l")[0])-1 workermen = int(public.ExecShell("ps aux|grep nginx|grep 'worker process'|awk '{memsum+=$6};END {print memsum}'")[0]) / 1024 @@ -66,8 +66,8 @@ class ajax: data['workermen'] = "%s%s" % (int(workermen), "MB") return data except Exception as ex: - public.WriteLog('GET_INFO','NGINX_LOAD_ERR',(ex,)) - return public.returnMsg(False,'GET_DATA_ERR') + public.write_log_gettext('Get Info','Nginx load status acquisition failed:{}',(ex,)) + return public.return_msg_gettext(False,'Data acquisition failed!') def GetPHPStatus(self,get): #取指定PHP版本的负载状态 @@ -80,8 +80,8 @@ class ajax: tmp['start time'] = time.strftime('%Y-%m-%d %H:%M:%S',fTime) return tmp except Exception as ex: - public.WriteLog('GET_INFO',"PHP_LOAD_ERR",(public.get_error_info(),)) - return public.returnMsg(False,'PHP_LOAD_ERR1') + public.write_log_gettext('Get Info',"PHP load status acquisition failed: {}",(public.get_error_info(),)) + return public.return_msg_gettext(False,'PHP load status acquisition failed!') def CheckStatusConf(self): if public.get_webserver() != 'nginx': return @@ -152,19 +152,19 @@ class ajax: def CheckLibInstall(self,checks): for cFile in checks: - if os.path.exists(cFile): return public.GetMsg("ALREADY_INSTALLED") - return public.GetMsg("NOT_INSTALL") + if os.path.exists(cFile): return public.GetMsg('Already installed') + return public.GetMsg('Not installed') #取插件操作选项 def GetLibOpt(self,status,libName): optStr = '' - if status == public.GetMsg("NOT_INSTALL"): - optStr = ''+public.GetMsg("INSTALL")+'' + if status == public.GetMsg('Not installed'): + optStr = ''+public.GetMsg('Uninstallaton succeeded')+'' else: - libConfig = public.GetMsg("CONF") - if(libName == 'beta'): libConfig = public.GetMsg("CLOSE_BETA") + libConfig = public.GetMsg('Old configuration') + if(libName == 'beta'): libConfig = public.GetMsg('Beta tester profile') - optStr = ''+libConfig+' | '+public.GetMsg("UNINSTALL")+''; + optStr = ''+libConfig+' | '+public.get_msg_gettext("Uninstallaton succeeded")+''; return optStr #取插件AS @@ -189,9 +189,9 @@ class ajax: result = public.ExecShell(public.get_python_bin() + " " + public.GetConfigValue('setup_path') + "/panel/script/backup_"+get.name+".py list") if result[0].find("ERROR:") == -1: - public.WriteLog("PLUG_MAM","SET_PLUG[" +info['name']+ "]AS!") - return public.returnMsg(True, 'SET_SUCCESS') - return public.returnMsg(False,'AK_SK_CONNECT_ERROR',(info['name'],)) + public.write_log_gettext("Plugin manager","Set plugin [" +info['name']+ "]AS!") + return public.return_msg_gettext(True, 'Successfully set') + return public.return_msg_gettext(False,'ERROR: Unable to connect to the {} server, please check if the [AK/SK/Storage] setting is correct!',(info['name'],)) #设置内测 def SetBeta(self,get): @@ -229,7 +229,7 @@ class ajax: result = public.ExecShell(public.get_python_bin() + " " + public.GetConfigValue('setup_path') + "/panel/script/backup_"+get.name+".py list") return json.loads(result[0]) except: - return public.returnMsg(False, 'GET_QINIU_FILE_LIST') + return public.return_msg_gettext(False, 'Failed to get the list, please check if the [AK/SK/Storage] setting is correct!') @@ -301,11 +301,11 @@ class ajax: import psutil p = psutil.Process(int(get.pid)) name = p.name() - if name == 'python': return public.returnMsg(False,'KILL_PROCESS_ERR') + if name == 'python': return public.return_msg_gettext(False,'Error, cannot end task processes!') p.kill() - public.WriteLog('TYPE_PROCESS','KILL_PROCESS',(get.pid,name)) - return public.returnMsg(True,'KILL_PROCESS',(get.pid,name)) + public.write_log_gettext('Task manager','Ended processes[{}][{}] Successfully!',(get.pid,name)) + return public.return_msg_gettext(True,'Ended processes[{}][{}] Successfully!',(get.pid,name)) def GoToProcess(self,name): ps = ['sftp-server','login','nm-dispatcher','irqbalance','qmgr','wpa_supplicant','lvmetad','auditd','master','dbus-daemon','tapdisk','sshd','init','ksoftirqd','kworker','kmpathd','kmpath_handlerd','python','kdmflush','bioset','crond','kthreadd','migration','rcu_sched','kjournald','iptables','systemd','network','dhclient','systemd-journald','NetworkManager','systemd-logind','systemd-udevd','polkitd','tuned','rsyslogd'] @@ -445,8 +445,8 @@ class ajax: public.writeFile('/www/server/panel/data/is_beta.pl','true') try: return {'status': True, 'msg': "Successful application!"} - except: return public.returnMsg(False,'AJAX_CONN_ERR') - except: return public.returnMsg(False,'AJAX_USER_BINDING_ERR') + except: return public.return_msg_gettext(False,'Fail to connect to the server!') + except: return public.return_msg_gettext(False,'Please bind your account first!') def to_not_beta(self,get): try: @@ -461,8 +461,8 @@ class ajax: if os.path.exists(beta_file): os.remove(beta_file) return {"status": True, "msg": "Successful application!"} - except: return public.returnMsg(False,'AJAX_CONN_ERR') - except: return public.returnMsg(False,'AJAX_USER_BINDING_ERR') + except: return public.return_msg_gettext(False,'Fail to connect to the server!') + except: return public.return_msg_gettext(False,'Please bind your account first!') def to_beta(self): try: @@ -483,11 +483,10 @@ class ajax: #获取最新的5条测试版更新日志 def get_beta_logs(self,get): try: - # data = json.loads(public.HttpGet('https://console.aapanel.com/api/panel/get_beta_logs_en')) data = json.loads(public.HttpGet('{}/api/panel/getBetaVersionLogs'.format(self.__official_url))) return data except: - return public.returnMsg(False,'AJAX_CONN_ERR') + return public.return_msg_gettext(False,'Fail to connect to the server!') def get_other_info(self): other = {} @@ -502,13 +501,11 @@ class ajax: def UpdatePanel(self,get): try: - if not public.IsRestart(): return public.returnMsg(False,'EXEC_ERR_TASK') + if not public.IsRestart(): return public.return_msg_gettext(False,'Please run the program when all install tasks finished!') import json conf_status = public.M('config').where("id=?",('1',)).field('status').find() if int(session['config']['status']) == 0 and int(conf_status['status']) == 0: - # public.HttpGet('{}/api/setupCount/setupPanel?type=Linux'.format(self.__official_url)) public.arequests('get', '{}/api/setupCount/setupPanel?type=Linux'.format(self.__official_url)) - public.M('config').where("id=?",('1',)).setField('status',1) #取回远程版本信息 @@ -535,21 +532,22 @@ class ajax: data['oem'] = '' data['intrusion'] = 0 data['uid'] = self.get_uid() - #msg = public.getMsg('PANEL_UPDATE_MSG'); + #msg = public.getMsg('Current version is stable version and already latest. Update cycle of stable version is generally 2 months,while developer version will update every Wednesday!'); data['o'] = public.get_oem_name() sUrl = '{}/api/panel/updateLinuxEn'.format(self.__official_url) updateInfo = json.loads(public.httpPost(sUrl,data)) - if not updateInfo: return public.returnMsg(False,"CONNECT_ERR") + if not updateInfo: return public.return_msg_gettext(False,'Failed to connect server!') #updateInfo['msg'] = msg; if os.path.exists('/www/server/panel/data/is_beta.pl'): updateInfo['is_beta'] = 1 session['updateInfo'] = updateInfo #检查是否需要升级 - if updateInfo['is_beta'] == 1: - if updateInfo['beta']['version'] ==session['version']: return public.returnMsg(False,updateInfo) - else: - if updateInfo['version'] ==session['version']: return public.returnMsg(False,updateInfo) + if not hasattr(get,'toUpdate'): + if updateInfo['is_beta'] == 1: + if updateInfo['beta']['version'] == session['version']: return public.returnMsg(False,updateInfo) + else: + if updateInfo['version'] == session['version']: return public.returnMsg(False,updateInfo) #是否执行升级程序 @@ -560,9 +558,9 @@ class ajax: httpUrl = public.get_url() if httpUrl: updateInfo['downUrl'] = httpUrl + '/install/' + uptype + '/LinuxPanel_EN-' + updateInfo['version'] + '.zip' public.downloadFile(updateInfo['downUrl'],'panel.zip') - if os.path.getsize('panel.zip') < 1048576: return public.returnMsg(False,"PANEL_UPDATE_ERR_DOWN") + if os.path.getsize('panel.zip') < 1048576: return public.return_msg_gettext(False,'File download failed, please try again or update manually!') public.ExecShell('unzip -o panel.zip -d ' + setupPath + '/') - import compileall + # import compileall if os.path.exists('/www/server/panel/runserver.py'): public.ExecShell('rm -f /www/server/panel/*.pyc') if os.path.exists('/www/server/panel/class/common.py'): public.ExecShell('rm -f /www/server/panel/class/*.pyc') @@ -572,7 +570,7 @@ class ajax: if updateInfo['is_beta'] == 1: self.to_beta() public.ExecShell("/etc/init.d/bt start") public.writeFile('data/restart.pl','True') - return public.returnMsg(True,'PANEL_UPDATE',(updateInfo['version'],)) + return public.return_msg_gettext(True,'Successful to update to {}',(updateInfo['version'],)) #输出新版本信息 data = { @@ -580,13 +578,20 @@ class ajax: 'version': updateInfo['version'], 'updateMsg' : updateInfo['updateMsg'] } - + # 输出忽略的版本 + updateInfo['ignore'] = [] + no_path = '{}/data/no_update.pl'.format(public.get_panel_path()) + if os.path.exists(no_path): + try: + updateInfo['ignore'] = json.loads(public.readFile(no_path)) + except: + pass public.ExecShell('rm -rf /www/server/phpinfo/*') return public.returnMsg(True,updateInfo) except Exception as ex: return public.get_error_info() - return public.returnMsg(False,"CONNECT_ERR") - + return public.return_msg_gettext(False,'Failed to connect server!') + #检查是否安装任何 def CheckInstalled(self,get): checks = ['nginx','apache','php','pure-ftpd','mysql'] @@ -611,7 +616,7 @@ class ajax: filename = '/usr/local/lsws/lsphp{}/etc/php/{}.{}/litespeed/php.ini'.format(get.version,get.version[0],get.version[1]) if os.path.exists('/etc/redhat-release'): filename = '/usr/local/lsws/lsphp' + get.version + '/etc/php.ini' - if not os.path.exists(filename): return public.returnMsg(False,'PHP_NOT_EXISTS') + if not os.path.exists(filename): return public.return_msg_gettext(False,'Requested PHP version does NOT exist!') phpini = public.readFile(filename) data = {} rep = "disable_functions\s*=\s{0,1}(.*)\n" @@ -698,7 +703,7 @@ class ajax: # 下载云端php扩展配置 def _get_cloud_phplib(self): - if not session.get('download_url'): session['download_url'] = 'http://node.aapanel.com' + if not session.get('download_url'): session['download_url'] = 'https://node.aapanel.com' download_url = session['download_url'] + '/install/lib/phplib_en.json' tstr = public.httpGet(download_url) data = json.loads(tstr) @@ -723,13 +728,13 @@ class ajax: #清理日志 def delClose(self,get): if not 'uid' in session: session['uid'] = 1 - if session['uid'] != 1: return public.returnMsg(False,'PERMISSION_DENIED') + if session['uid'] != 1: return public.return_msg_gettext(False,'Permission denied!') if 'tmp_login_id' in session: - return public.returnMsg(False,'PERMISSION_DENIED') + return public.return_msg_gettext(False,'Permission denied!') public.M('logs').where('id>?',(0,)).delete() - public.WriteLog('TYPE_CONFIG','LOG_CLOSE') - return public.returnMsg(True,'LOG_CLOSE') + public.write_log_gettext('Panel setting','Panel Logs emptied!') + return public.return_msg_gettext(True,'Panel Logs emptied!') def __get_webserver_conffile(self): webserver = public.get_webserver() @@ -778,54 +783,54 @@ class ajax: # 修改php ssl端口 def change_phpmyadmin_ssl_port(self,get): if public.get_webserver() == "openlitespeed": - return public.returnMsg(False, 'NOT_SUPPORT_OLS') + return public.return_msg_gettext(False, 'The current web server is openlitespeed. This function is not supported yet.') import re try: port = int(get.port) if 1 > port > 65535: - return public.returnMsg(False, 'PORT_CHECK_RANGE') + return public.return_msg_gettext(False, 'Port range is incorrect!') except: - return public.returnMsg(False, 'PORT_FORMAT_ERR') + return public.return_msg_gettext(False, 'Please enter the correct port number') for i in ["nginx","apache"]: file = "/www/server/panel/vhost/{}/phpmyadmin.conf".format(i) conf = public.readFile(file) if not conf: - return public.returnMsg(False,"PHPMYADMIN_SSL_ERR",(i,)) + return public.return_msg_gettext(False,'Did not find the {} configuration file, please try to close the ssl port settings before opening',(i,)) rulePort = ['80', '443', '21', '20', '8080', '8081', '8089', '11211', '6379'] if get.port in rulePort: - return public.returnMsg(False, 'AJAX_PHPMYADMIN_PORT_ERR') + return public.return_msg_gettext(False, 'Please do NOT use the usual port as the phpMyAdmin port!') if i == "nginx": if not os.path.exists("/www/server/panel/vhost/apache/phpmyadmin.conf"): - return public.returnMsg(False, "PHPMYADMIN_SSL_ERR1") + return public.return_msg_gettext(False, 'Did not find the apache phpmyadmin ssl configuration file, please try to close the ssl port settings before opening') rep = "listen\s*([0-9]+)\s*.*;" oldPort = re.search(rep, conf) if not oldPort: - return public.returnMsg(False, 'PHPMYADMIN_SSL_ERR2') + return public.return_msg_gettext(False, 'Did not detect the port that nginx phpmyadmin listens, please confirm whether the file has been manually modified.') oldPort = oldPort.groups()[0] conf = re.sub(rep, 'listen ' + get.port + ' ssl;', conf) else: rep = "Listen\s*([0-9]+)\s*\n" oldPort = re.search(rep, conf) if not oldPort: - return public.returnMsg(False, 'PHPMYADMIN_SSL_ERR3') + return public.return_msg_gettext(False, 'Did not detect the port that apache phpmyadmin listens, please confirm whether the file has been manually modified.') oldPort = oldPort.groups()[0] conf = re.sub(rep, "Listen " + get.port + "\n", conf, 1) rep = "VirtualHost\s*\*:[0-9]+" conf = re.sub(rep, "VirtualHost *:" + get.port, conf, 1) - if oldPort == get.port: return public.returnMsg(False, 'SOFT_PHPVERSION_ERR_PORT') + if oldPort == get.port: return public.return_msg_gettext(False, 'Port [{}] is in use!',(get.port,)) public.writeFile(file, conf) public.serviceReload() if i=="apache": import firewalls - get.ps = public.getMsg('SOFT_PHPVERSION_PS') + get.ps = public.getMsg('New phpMyAdmin Port') fw = firewalls.firewalls() fw.AddAcceptPort(get) public.serviceReload() - public.WriteLog('TYPE_SOFT', 'SOFT_PHPMYADMIN_PORT', (get.port,)) + public.write_log_gettext('Software manager', 'Modified access port to {} for phpMyAdmin!', (get.port,)) get.id = public.M('firewall').where('port=?', (oldPort,)).getField('id') get.port = oldPort fw.DelAcceptPort(get) - return public.returnMsg(True, 'SET_PORT_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') def _get_phpmyadmin_auth(self): import re @@ -846,9 +851,9 @@ class ajax: # 设置phpmyadmin ssl def set_phpmyadmin_ssl(self,get): if public.get_webserver() == "openlitespeed": - return public.returnMsg(False, 'NOT_SUPPORT_OLS') + return public.return_msg_gettext(False, 'The current web server is openlitespeed. This function is not supported yet.') if not os.path.exists("/www/server/panel/ssl/certificate.pem"): - return public.returnMsg(False,'PHPMYADMIN_SSL_ERR4') + return public.return_msg_gettext(False,'The panel certificate does not exist. Please apply for the panel certificate and try again.') if get.v == "1": # 获取auth信息 auth = "" @@ -952,9 +957,9 @@ class ajax: if os.path.exists("/www/server/panel/vhost/apache/phpmyadmin.conf"): os.remove("/www/server/panel/vhost/apache/phpmyadmin.conf") public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') public.serviceReload() - return public.returnMsg(True,'PHPMYADMIN_SSL_ERR5') + return public.return_msg_gettext(True,'Open successfully, please manually release phpmyadmin ssl port') #设置PHPMyAdmin @@ -965,13 +970,13 @@ class ajax: if public.get_webserver() == 'openlitespeed': filename = "/www/server/panel/vhost/openlitespeed/detail/phpmyadmin.conf" conf = public.readFile(filename) - if not conf: return public.returnMsg(False,'ERROR') + if not conf: return public.return_msg_gettext(False,'Operation failed') if hasattr(get,'port'): mainPort = public.readFile('data/port.pl').strip() rulePort = ['80','443','21','20','8080','8081','8089','11211','6379'] oldPort = "888" if get.port in rulePort: - return public.returnMsg(False,'AJAX_PHPMYADMIN_PORT_ERR') + return public.return_msg_gettext(False,'Please do NOT use the usual port as the phpMyAdmin port!') if public.get_webserver() == 'nginx': rep = r"listen\s+([0-9]+)\s*;" oldPort = re.search(rep,conf).groups()[0] @@ -990,19 +995,19 @@ class ajax: if tmp: oldPort = tmp.groups(1) conf = re.sub(reg,"address *:{}".format(get.port),conf) - if oldPort == get.port: return public.returnMsg(False,'SOFT_PHPVERSION_ERR_PORT') + if oldPort == get.port: return public.returnMsg(False,'Port [{}] is in use!',(get.port,)) public.writeFile(filename,conf) import firewalls - get.ps = public.getMsg('SOFT_PHPVERSION_PS') + get.ps = public.getMsg('New phpMyAdmin Port') fw = firewalls.firewalls() fw.AddAcceptPort(get) public.serviceReload() - public.WriteLog('TYPE_SOFT','SOFT_PHPMYADMIN_PORT',(get.port,)) + public.write_log_gettext('Software manager','Modified access port to {} for phpMyAdmin!',(get.port,)) get.id = public.M('firewall').where('port=?',(oldPort,)).getField('id') get.port = oldPort fw.DelAcceptPort(get) - return public.returnMsg(True,'SET_PORT_SUCCESS') + return public.returnMsg(True,'Setup successfully!') if hasattr(get,'phpversion'): if public.get_webserver() == 'nginx': @@ -1018,8 +1023,8 @@ class ajax: conf = re.sub(reg,'/usr/local/lsws/lsphp{}/bin/lsphp'.format(get.phpversion),conf) public.writeFile(filename,conf) public.serviceReload() - public.WriteLog('TYPE_SOFT','SOFT_PHPMYADMIN_PHP',(get.phpversion,)) - return public.returnMsg(True,'SOFT_PHPVERSION_SET') + public.write_log_gettext('Software manager','Modified PHP runtime version to PHP-{} for phpMyAdmin!',(get.phpversion,)) + return public.return_msg_gettext(True,'Setup successfully!') if hasattr(get,'password'): import panelSite @@ -1029,17 +1034,49 @@ class ajax: return panelSite.panelSite().SetHasPwd(get) if hasattr(get,'status'): - if conf.find(public.GetConfigValue('setup_path') + '/stop') != -1: - conf = conf.replace(public.GetConfigValue('setup_path') + '/stop',public.GetConfigValue('setup_path') + '/phpmyadmin') + pma_path = public.GetConfigValue('setup_path') + '/phpmyadmin' + stop_path = public.GetConfigValue('setup_path') + '/stop' + + + webserver = public.get_webserver() + if conf.find(stop_path) != -1: + conf = conf.replace(stop_path,pma_path) msg = public.getMsg('START') + + if webserver == 'nginx': + sub_string = '''{}; + allow 127.0.0.1; + allow ::1; + deny all'''.format(pma_path) + if conf.find(sub_string) != -1: + conf = conf.replace(sub_string,pma_path) + msg = public.getMsg('START') + else: + conf = conf.replace(pma_path,sub_string) + msg = public.getMsg('STOP') + elif webserver == 'apache': + src_string = 'AllowOverride All' + sub_string = '''{} + Deny from all + Allow from 127.0.0.1 ::1 localhost'''.format(src_string,pma_path) + if conf.find(sub_string) != -1: + conf = conf.replace(sub_string,src_string) + msg = public.getMsg('START') + else: + conf = conf.replace(src_string,sub_string) + msg = public.getMsg('STOP') else: - conf = conf.replace(public.GetConfigValue('setup_path') + '/phpmyadmin',public.GetConfigValue('setup_path') + '/stop') - msg = public.getMsg('STOP') + if conf.find(stop_path) != -1: + conf = conf.replace(stop_path,pma_path) + msg = public.getMsg('START') + else: + conf = conf.replace(pma_path,stop_path) + msg = public.getMsg('STOP') public.writeFile(filename,conf) public.serviceReload() - public.WriteLog('TYPE_SOFT','SOFT_PHPMYADMIN_STATUS',(msg,)) - return public.returnMsg(True,'SOFT_PHPMYADMIN_STATUS',(msg,)) + public.write_log_gettext('Software manager','phpMyAdmin already {}!',(msg,)) + return public.return_msg_gettext(True,'phpMyAdmin already {}!',(msg,)) #except: #return public.returnMsg(False,'ERROR'); @@ -1060,8 +1097,8 @@ class ajax: #保存PHP排序 def phpSort(self,get): - if public.writeFile('/www/server/php/sort.pl',get.ssort): return public.returnMsg(True,'SUCCESS') - return public.returnMsg(False,'ERROR') + if public.writeFile('/www/server/php/sort.pl',get.ssort): return public.return_msg_gettext(True,'Setup successfully!') + return public.return_msg_gettext(False,'Operation failed') #获取广告代码 def GetAd(self,get): @@ -1081,7 +1118,7 @@ class ajax: #获取警告标识 def GetWarning(self,get): warningFile = 'data/warning.json' - if not os.path.exists(warningFile): return public.returnMsg(False,'AJAX_WARNING_ERR') + if not os.path.exists(warningFile): return public.return_msg_gettext(False,'Warning list does NOT exist!') import json,time; wlist = json.loads(public.readFile(warningFile)) wlist['time'] = int(time.time()) @@ -1099,7 +1136,7 @@ class ajax: warningFile = 'data/warning.json' public.writeFile(warningFile,json.dumps(wlist)) - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #获取memcached状态 def GetMemcachedStatus(self,get): @@ -1139,7 +1176,7 @@ class ajax: conf = re.sub('CACHESIZE=\d+','CACHESIZE='+get.cachesize,conf) public.writeFile(confFile,conf) public.ExecShell(confFile + ' reload') - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #取redis状态 def GetRedisStatus(self,get): @@ -1180,10 +1217,10 @@ class ajax: def GetFpmLogs(self,get): import re fpm_path = '/www/server/php/' + get.version + '/etc/php-fpm.conf' - if not os.path.exists(fpm_path): return public.returnMsg(False,'AJAX_LOG_FILR_NOT_EXISTS') + if not os.path.exists(fpm_path): return public.return_msg_gettext(False,'Log file does NOT exist!') fpm_conf = public.readFile(fpm_path) log_tmp = re.findall(r"error_log\s*=\s*(.+)",fpm_conf) - if not log_tmp: return public.returnMsg(False,'AJAX_LOG_FILR_NOT_EXISTS') + if not log_tmp: return public.return_msg_gettext(False,'Log file does NOT exist!') log_file = log_tmp[0].strip() if log_file.find('var/log') == 0: log_file = '/www/server/php/' +get.version + '/'+ log_file @@ -1193,10 +1230,10 @@ class ajax: def GetFpmSlowLogs(self,get): import re fpm_path = '/www/server/php/' + get.version + '/etc/php-fpm.conf' - if not os.path.exists(fpm_path): return public.returnMsg(False,'AJAX_LOG_FILR_NOT_EXISTS') + if not os.path.exists(fpm_path): return public.return_msg_gettext(False,'Log file does NOT exist!') fpm_conf = public.readFile(fpm_path) log_tmp = re.findall(r"slowlog\s*=\s*(.+)",fpm_conf) - if not log_tmp: return public.returnMsg(False,'AJAX_LOG_FILR_NOT_EXISTS') + if not log_tmp: return public.return_msg_gettext(False,'Log file does NOT exist!') log_file = log_tmp[0].strip() if log_file.find('var/log') == 0: log_file = '/www/server/php/' +get.version + '/'+ log_file @@ -1204,12 +1241,120 @@ class ajax: #取指定日志 def GetOpeLogs(self,get): - if not os.path.exists(get.path): return public.returnMsg(False,'AJAX_LOG_FILR_NOT_EXISTS') + if not os.path.exists(get.path): return public.return_msg_gettext(False,'Log file does NOT exist!') return public.returnMsg(True,public.GetNumLines(get.path,1000)) - + + def get_pd(self,get): + from BTPanel import cache + tmp = -1 + try: + import panelPlugin + # get = public.dict_obj() + # get.init = 1 + tmp1 = panelPlugin.panelPlugin().get_cloud_list(get) + except: + tmp1 = None + if tmp1: + tmp = tmp1[public.to_string([112, 114, 111])] + ltd = tmp1.get('ltd', -1) + else: + ltd = -1 + tmp4 = cache.get(public.to_string([112, 95, 116, 111, 107, 101, 110])) + if tmp4: + tmp_f = public.to_string([47, 116, 109, 112, 47]) + tmp4 + if not os.path.exists(tmp_f): public.writeFile(tmp_f, '-1') + tmp = public.readFile(tmp_f) + if tmp: tmp = int(tmp) + if not ltd: ltd = -1 + if tmp == None: tmp = -1 + if ltd < 1: + if ltd == -2: + tmp3 = public.to_string( + [60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, 108, 116, 100, + 45, 103, 114, 97, 121, 34, 62, 60, 115, 112, 97, 110, 32, 115, 116, 121, 108, 101, + 61, 34, 99, 111, 108, 111, 114, 58, 32, 35, 102, 99, 54, 100, 50, 54, 59, 102, 111, + 110, 116, 45, 119, 101, 105, 103, 104, 116, 58, 32, 98, 111, 108, 100, 59, 109, 97, + 114, 103, 105, 110, 45, 114, 105, 103, 104, 116, 58, 53, 112, 120, 34, 62, 24050, 36807, + 26399, 60, 47, 115, 112, 97, 110, 62, 60, 97, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, + 108, 105, 110, 107, 34, 32, 111, 110, 99, 108, 105, 99, 107, 61, 34, 98, 116, 46, 115, 111, + 102, 116, 46, 117, 112, 100, 97, 116, 97, 95, 108, 116, 100, 40, 41, 34, 62, 82, 69, 78, 69, 87, + 60, 47, 97, + 62, 60, 47, 115, 112, 97, 110, 62]) + elif tmp == -1: + tmp3 = public.to_string([60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, + 116, 112, 114, 111, 45, 102, 114, 101, 101, 34, 32, 111, 110, 99, 108, 105, 99, + 107, + 61, 34, 98, 116, 46, 115, 111, 102, 116, 46, 114, 101, 110, 101, 119, 95, 112, + 114, + 111, 40, 41, 34, 32, 116, 105, 116, 108, 101, 61, 34, 67, 108, 105, 99, 107, + 32, 116, 111, 32, + 103, 101, 116, 32, 80, 82, 79, 34, 62, 20813, 36153, 29256, 60, 47, 115, 112, + 97, 110, 62]) + elif tmp == -2: + tmp3 = public.to_string([60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, + 112, 114, 111, 45, 103, 114, 97, 121, 34, 62, 60, 115, 112, 97, 110, 32, + 115, 116, 121, 108, 101, 61, 34, 99, 111, 108, 111, 114, 58, 32, 35, + 102, 99, 54, 100, 50, 54, 59, 102, 111, 110, 116, 45, 119, 101, 105, 103, + 104, 116, 58, 32, 98, 111, 108, 100, 59, 109, 97, 114, 103, 105, 110, 45, + 114, 105, 103, 104, 116, 58, 53, 112, 120, 34, 62, 24050, 36807, 26399, + 60, 47, 115, 112, 97, 110, 62, 60, 97, 32, 99, 108, 97, 115, 115, 61, 34, + 98, 116, 108, 105, 110, 107, 34, 32, 111, 110, 99, 108, 105, 99, 107, 61, + 34, 98, 116, 46, 115, 111, 102, 116, 46, 114, 101, 110, 101, 119, 95, 112, 114, + 111, 40, 41, 34, 62, 82, 69, 78, 69, 87, 60, 47, 97, 62, 60, 47, 115, 112, 97, + 110, 62]) + if tmp >= 0 and ltd in [-1, -2]: + if tmp == 0: + tmp2 = public.to_string([27704, 20037, 25480, 26435]) + tmp3 = public.to_string([60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, + 112, 114, 111, 34, 62, 123, 48, 125, 60, 115, 112, 97, 110, 32, 115, 116, + 121, 108, 101, 61, 34, 99, 111, 108, 111, 114, 58, 32, 35, 102, 99, 54, + 100, + 50, 54, 59, 102, 111, 110, 116, 45, 119, 101, 105, 103, 104, 116, + 58, 32, 98, 111, 108, 100, 59, 34, 62, 123, 49, 125, 60, 47, 115, + 112, 97, 110, 62, 60, 47, 115, 112, 97, 110, 62]).format( + public.to_string([21040, 26399, 26102, 38388, 65306]), tmp2) + else: + tmp2 = time.strftime(public.to_string([37, 89, 45, 37, 109, 45, 37, 100]), time.localtime(tmp)) + tmp3 = public.to_string([60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, + 112, 114, 111, 34, 62, 69, 120, 112, 105, 114, 101, 58, 32, 60, 115, 112, + 97, 110, 32, 115, 116, 121, 108, 101, 61, 34, 99, 111, 108, 111, 114, + 58, 32, 35, 102, 99, 54, 100, 50, 54, 59, 102, 111, 110, 116, 45, 119, + 101, 105, 103, 104, 116, 58, 32, 98, 111, 108, 100, 59, 109, 97, 114, + 103, 105, 110, 45, 114, 105, 103, 104, 116, 58, 53, 112, 120, 34, 62, 123, + 48, 125, 60, 47, 115, 112, 97, 110, 62, 60, 97, 32, 99, 108, 97, 115, + 115, 61, 34, 98, 116, 108, 105, 110, 107, 34, 32, 111, 110, 99, 108, 105, + 99, + 107, 61, 34, 98, 116, 46, 115, 111, 102, 116, 46, 114, 101, 110, 101, 119, + 95, + 112, 114, 111, 40, 41, 34, 62, 82, 69, 78, 69, 87, 60, 47, 97, 62, 60, + 47, 115, 112, 97, 110, 62]).format(tmp2) + else: + tmp3 = public.to_string([60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, 112, + 114, 111, 45, 103, 114, 97, 121, 34, 32, 111, 110, 99, 108, 105, 99, 107, + 61, 34, 98, 116, 46, 115, 111, 102, 116, 46, 117, 112, 100, 97, 116, 97, 95, 112, + 114, 111, 40, 41, 34, 32, 116, 105, 116, 108, 101, 61, 34, 67, 108, 105, 99, + 107, 32, 116, + 111, 32, 103, 101, 116, 32, 80, 82, 79, 34, 62, 70, 82, + 69, 69, 60, 47, 115, 112, 97, 110, 62]) + else: + tmp3 = public.to_string([60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, 108, 116, + 100, 34, 62, 69, 120, 112, 105, 114, 101, 58, 32, 60, 115, 112, 97, 110, 32, 115, + 116, + 121, 108, 101, 61, 34, 99, 111, 108, 111, 114, 58, 32, 35, 102, 99, 54, 100, 50, + 54, 59, 102, 111, 110, 116, 45, 119, 101, 105, 103, 104, 116, 58, 32, 98, 111, + 108, 100, 59, 109, 97, 114, 103, 105, 110, 45, 114, 105, 103, 104, 116, 58, 53, + 112, 120, 34, 62, 123, 125, 60, 47, 115, 112, 97, 110, 62, 60, 97, 32, 99, 108, + 97, 115, 115, 61, 34, 98, 116, 108, 105, 110, 107, 34, 32, 111, 110, 99, 108, 105, + 99, 107, 61, 34, 98, 116, 46, 115, 111, 102, 116, 46, 114, 101, 110, 101, 119, 95, + 112, 114, 111, 40, 41, 34, 62, 82, 69, 78, 69, 87, 60, 47, 97, 62, 60, 47, 115, + 112, 97, 110, 62]).format( + time.strftime(public.to_string([37, 89, 45, 37, 109, 45, 37, 100]), time.localtime(ltd))) + + return tmp3, tmp, ltd + #检查用户绑定是否正确 def check_user_auth(self,get): - import requests + # import requests m_key = 'check_user_auth' if m_key in session: return session[m_key] u_path = 'data/userInfo.json' @@ -1217,15 +1362,16 @@ class ajax: userInfo = json.loads(public.ReadFile(u_path)) except: if os.path.exists(u_path): os.remove(u_path) - return public.returnMsg(False,'AJAX_USER_BE_OVERDUE') + return public.return_msg_gettext(False,'Account binding has expired, please re-bind on the [Settings] page!') url_headers = {"authorization":"bt {}".format(userInfo['token'])} - resp = requests.post('{}/api/user/verifyToken'.format(self.__official_url),headers=url_headers,verify=False) + # resp = requests.post('{}/api/user/verifyToken'.format(self.__official_url),headers=url_headers,verify=False) + resp = public.HttpPost.post('{}/api/user/verifyToken'.format(self.__official_url), headers=url_headers, verify=False) resp = resp.json() if not resp['success']: if os.path.exists(u_path): os.remove(u_path) - return public.returnMsg(False,'AJAX_USER_BE_OVERDUE') + return public.return_msg_gettext(False,'Account binding has expired, please re-bind on the [Settings] page!') else: - session[m_key] = public.returnMsg(True,'AJAX_USER_IS_VALID') + session[m_key] = public.return_msg_gettext(True,'Binding is valid!') return session[m_key] @@ -1239,7 +1385,7 @@ class ajax: php_ini = php_path + php_version + '/etc/php.ini' if not os.path.exists('/etc/redhat-release') and public.get_webserver() == 'openlitespeed': php_ini = php_path + php_version + '/etc/php/'+args.php_version+'/litespeed/php.ini' - tmp = public.ExecShell(php_bin + ' /www/server/panel/class/php_info.php')[0] + tmp = public.ExecShell(php_bin + ' -c {} /www/server/panel/class/php_info.php'.format(php_ini))[0] if tmp.find('Warning: JIT is incompatible') != -1: tmp = tmp.strip().split('\n')[-1] result = json.loads(tmp) @@ -1258,8 +1404,113 @@ class ajax: #取指定行 def get_lines(self,args): - if not os.path.exists(args.filename): return public.returnMsg(False,'LOG_EMPTY') + if not os.path.exists(args.filename): return public.returnMsg(False,'Logs emptied') s_body = public.ExecShell("tail -n {} {}".format(args.num,args.filename))[0] return public.returnMsg(True,s_body) - \ No newline at end of file + def log_analysis(self,get): + import log_analysis + log_analysis=log_analysis.log_analysis() + return log_analysis.log_analysis(get) + + + def speed_log(self,get): + import log_analysis + log_analysis=log_analysis.log_analysis() + return log_analysis.speed_log(get) + + + + def get_result(self,get): + import log_analysis + log_analysis=log_analysis.log_analysis() + return log_analysis.get_result(get) + + def get_detailed(self,get): + import log_analysis + log_analysis=log_analysis.log_analysis() + return log_analysis.get_detailed(get) + + def download_pay_type(self, path): + public.downloadFile(public.get_url() + '/install/lib/pay_type_en.json', path) + return True + + def get_pay_type(self, get): + """ + @name 获取推荐列表 + """ + spath = '{}/data/pay_type.json'.format(public.get_panel_path()) + down = cache.get('pay_type') + if not down: + public.run_thread(self.download_pay_type, (spath,)) + cache.set('pay_type', 1, 86400) + try: + data = json.loads(public.readFile("data/pay_type.json")) + except: + public.run_thread(self.download_pay_type, (spath,)) + data = {} + + import panelPlugin + plu_panel = panelPlugin.panelPlugin() + plugin_list = plu_panel.get_cloud_list() + if not 'pro' in plugin_list: plugin_list['pro'] = -1 + + for item in data: + if 'list' in item: + item['list'] = self.__get_home_list(item['list'], item['type'], plugin_list, plu_panel) + if item['type'] == 1: + if len(item['list']) > 4: item['list'] = item['list'][:4] + # if item['type'] == 0 and plugin_list['pro'] >= 0: + # item['show'] = False + return data + + def __get_home_list(self, sList, stype, plugin_list, plu_panel): + """ + @name 获取首页软件列表推荐 + """ + nList = [] + webserver = public.get_webserver() + for x in sList: + for plugin_info in plugin_list['list']: + if x['name'] == plugin_info['name']: + if not 'endtime' in plugin_info or plugin_info['endtime'] >= 0: + x['isBuy'] = True + is_check = False + if 'dependent' in x: + if x['dependent'] == webserver: is_check = True + else: + is_check = True + if is_check: + info = plu_panel.get_soft_find(x['name']) + if info: + if stype == 1: + # if plugin_list['pro'] >= 0: continue + if not info['setup']: + x['install'] = info['setup'] + nList.append(x) + else: + x['install'] = info['setup'] + nList.append(x) + return nList + + def ignore_version(self, get): + """ + @忽略版本更新 + :param version 忽略的版本号 + """ + version = get.version + path = '{}/data/no_update.pl'.format(public.get_panel_path()) + try: + data = json.loads(public.readFile(path)) + except: + data = [] + + if not version in data: data.append(version) + + public.writeFile(path, json.dumps(data)) + try: + del (session['updateInfo']) + except: + pass + + return public.return_msg_gettext(True, "Ignore success, this version will no longer be reminded to update.") \ No newline at end of file diff --git a/class/apache.py b/class/apache.py index 2e67519b..2d4a34f3 100644 --- a/class/apache.py +++ b/class/apache.py @@ -43,7 +43,7 @@ class apache: try: workermen = int(public.ExecShell("ps aux|grep httpd|grep 'start'|awk '{memsum+=$6};END {print memsum}'")[0]) / 1024 except: - return public.returnMsg(False,"Get worker RAM False") + return public.return_msg_gettext(False,"Get worker RAM False") for proc in psutil.process_iter(): if proc.name() == "httpd": self.GetProcessCpuPercent(proc.pid,process_cpu) @@ -54,7 +54,7 @@ class apache: # 计算启动时间 Uptime = re.search("ServerUptimeSeconds:\s+(.*)",result) if not Uptime: - return public.returnMsg(False, "Get worker Uptime False") + return public.return_msg_gettext(False, "Get worker Uptime False") Uptime = int(Uptime.group(1)) min = Uptime / 60 hours = min / 60 @@ -65,16 +65,16 @@ class apache: #格式化重启时间 restarttime = re.search("RestartTime:\s+(.*)",result) if not restarttime: - return public.returnMsg(False, "Get worker Restart Time False") + return public.return_msg_gettext(False, "Get worker Restart Time False") restarttime = restarttime.group(1) rep = "\w+,\s([\w-]+)\s([\d\:]+)\s\w+" date = re.search(rep,restarttime) if not date: - return public.returnMsg(False, "Get worker date False") + return public.return_msg_gettext(False, "Get worker date False") date = date.group(1) timedetail = re.search(rep,restarttime) if not timedetail: - return public.returnMsg(False, "Get worker time detail False") + return public.return_msg_gettext(False, "Get worker time detail False") timedetail=timedetail.group(2) monthen = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"] n = 0 @@ -87,7 +87,7 @@ class apache: reqpersec = re.search("ReqPerSec:\s+(.*)", result) if not reqpersec: - return public.returnMsg(False, "Get worker reqpersec False") + return public.return_msg_gettext(False, "Get worker reqpersec False") reqpersec = reqpersec.group(1) if re.match("^\.", reqpersec): reqpersec = "%s%s" % (0,reqpersec) @@ -95,20 +95,20 @@ class apache: data["UpTime"] = "%s day %s hour %s minute" % (str(int(days)),str(int(hours)),str(int(min))) total_acc = re.search("Total Accesses:\s+(\d+)",result) if not total_acc: - return public.returnMsg(False, "Get worker TotalAccesses False") + return public.return_msg_gettext(False, "Get worker TotalAccesses False") data["TotalAccesses"] = total_acc.group(1) total_kb = re.search("Total kBytes:\s+(\d+)",result) if not total_kb: - return public.returnMsg(False, "Get worker TotalKBytes False") + return public.return_msg_gettext(False, "Get worker TotalKBytes False") data["TotalKBytes"] = total_kb.group(1) data["ReqPerSec"] = round(float(reqpersec), 2) busywork = re.search("BusyWorkers:\s+(\d+)",result) if not busywork: - return public.returnMsg(False, "Get worker BusyWorkers False") + return public.return_msg_gettext(False, "Get worker BusyWorkers False") data["BusyWorkers"] = busywork.group(1) idlework = re.search("IdleWorkers:\s+(\d+)",result) if not idlework: - return public.returnMsg(False, "Get worker IdleWorkers False") + return public.return_msg_gettext(False, "Get worker IdleWorkers False") data["IdleWorkers"] = idlework.group(1) data["workercpu"] = round(float(process_cpu["httpd"]),2) data["workermem"] = "%s%s" % (int(workermen),"MB") @@ -120,10 +120,10 @@ class apache: if not "mpm_event_module" in apachempmcontent: return public.returnMsg(False,"mpm_event_module conf not found or /www/server/apache/conf/extra/httpd-mpm.conf is empty") apachempmcontent = re.search("\(\n|.)+?\",apachempmcontent).group() - ps = ["%s,%s" % (public.GetMsg("SECOND"),public.GetMsg("REQUEST_TIMEOUT_TIME")), - public.GetMsg("KEEP_ALIVE"), - "%s,%s" % (public.GetMsg("SECOND"),public.GetMsg("CONNECT_TIMEOUT_TIME")), - public.GetMsg("MAX_KEEP_ALIVE_REQUESTS")] + ps = ["%s,%s" % (public.get_msg_gettext('Second'),public.get_msg_gettext('Request timeout')), + public.get_msg_gettext('Keep alive'), + "%s,%s" % (public.get_msg_gettext('Second'),public.get_msg_gettext('Connection timeout')), + public.get_msg_gettext('Max keep-alive requests per connection')] gets = ["Timeout","KeepAlive","KeepAliveTimeout","MaxKeepAliveRequests"] if public.get_webserver() == 'apache': shutil.copyfile(self.apachedefaultfile, '/tmp/apdefault_file_bk.conf') @@ -134,34 +134,34 @@ class apache: rep = "(%s)\s+(\w+)" % i k = re.search(rep, apachedefaultcontent) if not k: - return public.returnMsg(False, "Get Key {} False".format(k)) + return public.return_msg_gettext(False, "Get Key {} False",(i,)) k = k.group(1) v = re.search(rep, apachedefaultcontent) if not v: - return public.returnMsg(False, "Get Value {} False".format(v)) + return public.return_msg_gettext(False, "Get Value {} False",(v,)) v = v.group(2) psstr = ps[n] kv = {"name":k,"value":v,"ps":psstr} conflist.append(kv) n += 1 - ps = [public.GetMsg("DEFUALT_PROCESSES"), - public.GetMsg("MAX_SPARE_THREADS"), - public.GetMsg("MIN_SPARE_THREADS"), - public.GetMsg("THREADS_PER_CHILD"), - public.GetMsg("MAX_REQUEST_WORKERS"), - public.GetMsg("MaxConnectionsPerChild")] + ps = [public.get_msg_gettext('Default processes'), + public.get_msg_gettext('Maximum number of idle threads'), + public.get_msg_gettext('Minimum number of idle threads available to handle request spikes'), + public.get_msg_gettext('Number of threads created by each child process'), + public.get_msg_gettext('Maximum number of connections that will be processed simultaneously'), + public.get_msg_gettext('Limit on the number of connections that an individual child server will handle during its life')] gets = ["StartServers","MaxSpareThreads","MinSpareThreads","ThreadsPerChild","MaxRequestWorkers","MaxConnectionsPerChild"] n = 0 for i in gets: rep = "(%s)\s+(\w+)" % i k = re.search(rep, apachempmcontent) if not k: - return public.returnMsg(False, "Get Key {} False".format(k)) + return public.return_msg_gettext(False, "Get Key {} False",(i,)) k = k.group(1) v = re.search(rep, apachempmcontent) if not v: - return public.returnMsg(False, "Get Value {} False".format(v)) + return public.return_msg_gettext(False, "Get Value {} False",(v,)) v = v.group(2) psstr = ps[n] kv = {"name": k, "value": v, "ps": psstr} @@ -173,7 +173,7 @@ class apache: apachedefaultcontent = public.readFile(self.apachedefaultfile) apachempmcontent = public.readFile(self.apachempmfile) if not "mpm_event_module" in apachempmcontent: - return public.returnMsg(False,"mpm_event_module conf not found or /www/server/apache/conf/extra/httpd-mpm.conf is empty") + return public.return_msg_gettext(False,"mpm_event_module conf not found or /www/server/apache/conf/extra/httpd-mpm.conf is empty") conflist = [] getdict = get.__dict__ for i in getdict.keys(): @@ -186,12 +186,12 @@ class apache: for c in conflist: if c["name"] == "KeepAlive": if not re.search("on|off", c["value"]): - return public.returnMsg(False, "INIT_ARGS_ERR") + return public.return_msg_gettext(False, 'Parameter ERROR!') else: print(c["value"]) if not re.search("\d+", c["value"]): print(c["name"],c["value"]) - return public.returnMsg(False, 'INIT_ARGS_ERR') + return public.return_msg_gettext(False, 'Parameter ERROR!') rep = "%s\s+\w+" % c["name"] if re.search(rep,apachedefaultcontent): @@ -206,10 +206,10 @@ class apache: if (isError != True): shutil.copyfile('/tmp/_file_bk.conf', self.apachedefaultfile) shutil.copyfile('/tmp/proxyfile_bk.conf', self.apachempmfile) - return public.returnMsg(False, 'ERROR: %s
                                      ' % public.GetMsg("CONFIG_ERROR") + isError.replace("\n", + return public.returnMsg(False, 'ERROR: %s
                                      ' % public.get_msg_gettext('Configuration ERROR') + isError.replace("\n", '
                                      ') + '
                                      ') public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') def add_httpd_access_log_format(self,args): ''' @@ -232,12 +232,12 @@ class apache: self.del_httpd_access_log_format(args) conf = public.readFile(self.httpdconf) if not conf: - return public.returnMsg(False,'CONF_FILE_NOT_EXISTS') + return public.return_msg_gettext(False,'Configuration file not exist') reg = '' conf = re.sub(reg,''+data,conf) public.writeFile(self.httpdconf,conf) public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') except: return public.returnMsg(False, str(public.get_error_info())) @@ -249,13 +249,13 @@ class apache: ''' conf = public.readFile(self.httpdconf) if not conf: - return public.returnMsg(False, 'CONF_FILE_NOT_EXISTS') + return public.return_msg_gettext(False, 'Configuration file not exist') reg = '\s*#LOG_FORMAT_BEGIN_{n}(\n|.)+#LOG_FORMAT_END_{n}\n?'.format(n=args.log_format_name) conf = re.sub(reg,'',conf) self._del_format_log_of_website(args.log_format_name) public.writeFile(self.httpdconf,conf) public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') def del_all_log_format(self,args): all_format = self.get_httpd_access_log_format(args) @@ -318,7 +318,7 @@ class apache: reg = "#LOG_FORMAT_BEGIN.*" conf = public.readFile(self.httpdconf) if not conf: - return public.returnMsg(False, 'CONF_FILE_NOT_EXISTS') + return public.return_msg_gettext(False, 'Configuration file not exist') data = re.findall(reg,conf) format_name = [i.split('LOG_FORMAT_BEGIN_')[-1] for i in data] format_log = {} @@ -349,7 +349,7 @@ class apache: website_conf_file = '/www/server/panel/vhost/apache/{}.conf'.format(site['name']) conf = public.readFile(website_conf_file) if not conf: - return public.returnMsg(False, 'CONF_FILE_NOT_EXISTS') + return public.return_msg_gettext(False, 'Configuration file not exist') format_exist_reg = '(CustomLog\s+"/www.*\_log).*' access_log = re.search(format_exist_reg, conf).groups()[0] + '" ' + args.log_format_name if site['name'] not in sites and re.search(format_exist_reg,conf): @@ -360,7 +360,7 @@ class apache: conf = re.sub(format_exist_reg,access_log,conf) public.writeFile(website_conf_file,conf) public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') except: return public.returnMsg(False, str(public.get_error_info())) diff --git a/class/cachelib/simple.py b/class/cachelib/simple.py index a0723e9f..9a2577d6 100644 --- a/class/cachelib/simple.py +++ b/class/cachelib/simple.py @@ -40,6 +40,7 @@ class SimpleCache(BaseCache): toremove.append(key) for key in toremove: self._cache.pop(key, None) + self.del_session_by_file(key) def _normalize_timeout(self, timeout): @@ -48,70 +49,87 @@ class SimpleCache(BaseCache): timeout = time() + timeout return timeout - def get(self, key): - try: - - expires, value = self._cache[key] - if expires == 0 or expires > time(): - return pickle.loads(value) - - except (KeyError, pickle.PickleError): - try: - if key[:4] == self.__session_key: - filename = '/'.join((self.__session_basedir,self.md5(key))) - if not os.path.exists(filename): return None - - with open(filename, 'rb') as fp: - _val = fp.read() - fp.close() - - expires = struct.unpack('f',_val[:4])[0] - if expires == 0 or expires > time(): - value = _val[4:] - - self._cache[key] = (expires,value) - return pickle.loads(value) - except :pass - return None - - def set(self, key, value, timeout=None): - - expires = self._normalize_timeout(timeout) - self._prune() - - _val = pickle.dumps(value, pickle.HIGHEST_PROTOCOL) - self._cache[key] = (expires,_val) + def get_session_by_file(self,key): + try: + if key[:4] == self.__session_key: + filename = '/'.join((self.__session_basedir,self.md5(key))) + if not os.path.exists(filename): return None + + with open(filename, 'rb') as fp: + _val = fp.read() + fp.close() + expires = struct.unpack('f',_val[:4])[0] + if expires == 0 or expires > time(): + value = _val[4:] + + self._cache[key] = (expires,value) + return pickle.loads(value) + except :pass + + def set_session_by_file(self,key,_val,expires): try: if key[:4] == self.__session_key: - if len(_val) < 256: return True if not os.path.exists(self.__session_basedir): os.makedirs(self.__session_basedir,384) - expires = struct.pack('f',expires) - filename = '/'.join((self.__session_basedir,self.md5(key))) + filename = '/'.join((self.__session_basedir,self.md5(key))) fp = open(filename, 'wb+') fp.write(expires + _val) fp.close() os.chmod(filename,384) except :pass + + def del_session_by_file(self,key): + try: + if key[:4] == self.__session_key: + filename = '/'.join((self.__session_basedir,self.md5(key))) + if os.path.exists(filename): os.remove(filename) + except : pass + + def get(self, key): + if not isinstance(key,str): return None + try: + expires, value = self._cache[key] + if expires == 0 or expires > time(): + return pickle.loads(value) + except (KeyError, pickle.PickleError): + return self.get_session_by_file(key) + + def set(self, key, value, timeout=None): + + # 类型判断 + if not isinstance(key,str): return False + type_list=(int,float,bool,str,list,dict,tuple,set,bytes) + if not isinstance(value,type_list): return False + + # 过期清理 + expires = self._normalize_timeout(timeout) + self._prune() + + # 转换 + _val = pickle.dumps(value, pickle.HIGHEST_PROTOCOL) + self._cache[key] = (expires,_val) + self.set_session_by_file(key,_val,expires) return True def add(self, key, value, timeout=None): + + # 类型判断 + if not isinstance(key,str): return False + type_list=(int,float,bool,str,list,dict,tuple,set,bytes) + if not isinstance(value,type_list): return False + expires = self._normalize_timeout(timeout) self._prune() - item = (expires, pickle.dumps(value, - pickle.HIGHEST_PROTOCOL)) + item = (expires, pickle.dumps(value,pickle.HIGHEST_PROTOCOL)) if key in self._cache: return False self._cache.setdefault(key, item) + self.set_session_by_file(key,item[1],expires) return True def delete(self, key): result = self._cache.pop(key, None) is not None - try: - if key[:4] == self.__session_key: - filename = '/'.join((self.__session_basedir,self.md5(key))) - if os.path.exists(filename): os.remove(filename) - except : pass + self.del_session_by_file(key) return result def has(self, key): @@ -119,8 +137,17 @@ class SimpleCache(BaseCache): expires, value = self._cache[key] return expires == 0 or expires > time() except KeyError: + if self.get_session_by_file(key): return True return False + + def get_expire_time(self, key): + try: + expires, value = self._cache[key] + return expires + except KeyError: + return 0 + def md5(self,strings): """ 生成MD5 @@ -129,7 +156,7 @@ class SimpleCache(BaseCache): """ import hashlib m = hashlib.md5() - + m.update(strings.encode('utf-8')) return m.hexdigest() diff --git a/class/common.py b/class/common.py index 63b2bf88..eb4ce4a0 100644 --- a/class/common.py +++ b/class/common.py @@ -26,7 +26,7 @@ class panelSetup: ua = g.ua.lower() if ua.find('spider') != -1 or g.ua.find('bot') != -1: return redirect('https://www.google.com') - g.version = '6.8.21' + g.version = '6.8.27' g.title = public.GetConfigValue('title') g.uri = request.path g.debug = os.path.exists('data/debug.pl') @@ -98,9 +98,9 @@ class panelAdmin(panelSetup): if request.method == 'GET': g.menus = public.get_menus() g.yaer = datetime.now().year - session["top_tips"] = public.GetMsg("TOP_TIPS") - session["bt_help"] = public.GetMsg("BT_HELP") - session["download"] = public.GetMsg("DOWNLOAD") + session["top_tips"] = public.get_msg_gettext("The current IE browser version is too low to display some features, please use another browser. Or if you use a browser developed by a Chinese company, please switch to Extreme Mode!") + session["bt_help"] = public.get_msg_gettext("For Support|Suggestions, please visit the aaPanel Forum") + session["download"] = public.get_msg_gettext("Downloading:") if not 'brand' in session: session['brand'] = public.GetConfigValue('brand') session['product'] = public.GetConfigValue('product') @@ -114,7 +114,7 @@ class panelAdmin(panelSetup): if not 'lan' in session: session['lan'] = public.GetLanguage() if not 'home' in session: - session['home'] = 'https://brandnew.aapanel.com' + session['home'] = 'https://www.aapanel.com' return False # 检查Web服务器类型 @@ -150,7 +150,7 @@ class panelAdmin(panelSetup): if not 'login' in session: api_check = self.get_sk() if api_check: - #session.clear() + session.clear() return api_check g.api_request = True else: @@ -203,54 +203,61 @@ class panelAdmin(panelSetup): def get_sk(self): save_path = '/www/server/panel/config/api.json' if not os.path.exists(save_path): - return redirect('/login') + return public.error_not_login('/login') + + try: api_config = json.loads(public.ReadFile(save_path)) except: os.remove(save_path) - return redirect('/login') + return public.error_not_login('/login') if not api_config['open']: - return redirect('/login') + return public.error_not_login('/login') from BTPanel import get_input get = get_input() client_ip = public.GetClientIp() if not 'client_bind_token' in get: if not 'request_token' in get or not 'request_time' in get: - return redirect('/login') + return public.error_not_login('/login') num_key = client_ip + '_api' if not public.get_error_num(num_key,20): - return public.returnJson(False,'AUTH_FAILED1') + return public.returnJson(False,'20 consecutive verification failures, prohibited for 1 hour') - if not client_ip in api_config['limit_addr']: + if not public.is_api_limit_ip(api_config['limit_addr'],client_ip): #client_ip in api_config['limit_addr']: public.set_error_num(num_key) - return public.returnJson(False,'%s[' % public.GetMsg("AUTH_FAILED1")+client_ip+']') + return public.returnJson(False,'%s[' % public.get_msg_gettext("20 consecutive verification failures, prohibited for 1 hour")+client_ip+']') else: num_key = client_ip + '_app' if not public.get_error_num(num_key,20): - return public.returnJson(False,'AUTH_FAILED1') + return public.returnJson(False,'20 consecutive verification failures, prohibited for 1 hour') a_file = '/dev/shm/' + get.client_bind_token + + if not public.path_safe_check(get.client_bind_token): + public.set_error_num(num_key) + return public.returnJson(False, 'illegal request') + if not os.path.exists(a_file): import panelApi if not panelApi.panelApi().get_app_find(get.client_bind_token): public.set_error_num(num_key) - return public.returnJson(False,'UNBOUND_DEVICE') + return public.returnJson(False,'Unbound device') public.writeFile(a_file,'') if not 'key' in api_config: public.set_error_num(num_key) - return public.returnJson(False, 'KEY_ERR') + return public.returnJson(False, 'Key verification failed') if not 'form_data' in get: public.set_error_num(num_key) - return public.returnJson(False, 'FORM_DATA_ERR') + return public.returnJson(False, 'No form_data data found') g.form_data = json.loads(public.aes_decrypt(get.form_data, api_config['key'])) get = get_input() if not 'request_token' in get or not 'request_time' in get: - return redirect('/login') + return public.error_not_login('/login') g.is_aes = True g.aes_key = api_config['key'] request_token = public.md5(get.request_time + api_config['token']) @@ -258,7 +265,7 @@ class panelAdmin(panelSetup): public.set_error_num(num_key,True) return False public.set_error_num(num_key) - return public.returnJson(False,'SECRET_KEY_CHECK_FALSE') + return public.returnJson(False,'Secret key verification failed') # 检查系统配置 diff --git a/class/config.py b/class/config.py index c0ec2ec3..a5018ee8 100644 --- a/class/config.py +++ b/class/config.py @@ -6,7 +6,8 @@ # +------------------------------------------------------------------- # | Author: hwliang # +------------------------------------------------------------------- -import public,re,sys,os,nginx,apache,json,time,ols +import base64 +import public,re,os,nginx,apache,json,time,ols try: import pyotp except: @@ -43,7 +44,7 @@ class config: except:pass # 返回配置邮件地址 def return_mail_list(self, get): - return public.returnMsg(True, self.__mail_list) + return public.return_msg_gettext(True, self.__mail_list) # 删除邮件接口 def del_mail_list(self, get): @@ -51,9 +52,9 @@ class config: if emial in self.__mail_list: self.__mail_list.remove(emial) public.writeFile(self.__mail_list_data, json.dumps(self.__mail_list)) - return public.returnMsg(True, 'DEL_SUCCESS') + return public.return_msg_gettext(True, 'Successfully deleted') else: - return public.returnMsg(True, 'EMAIL_NOT_EXISTS') + return public.return_msg_gettext(True, 'Email does not exist') def del_tg_info(self,get): import panel_telegram_bot @@ -65,39 +66,39 @@ class config: #添加接受邮件地址 def add_mail_address(self, get): - if not hasattr(get, 'email'): return public.returnMsg(False, 'INPUT_EMAIL') + if not hasattr(get, 'email'): return public.return_msg_gettext(False, 'Please input your email') emailformat = re.compile(r'[a-zA-Z0-9.-_+%]+@[a-zA-Z0-9]+\.[a-zA-Z0-9]+') - if not emailformat.search(get.email): return public.returnMsg(False, 'EMAIL_ERR') + if not emailformat.search(get.email): return public.return_msg_gettext(False, 'Please enter your vaild email') # 测试发送邮件 - if get.email.strip() in self.__mail_list: return public.returnMsg(True, 'EMAIL_EXISTS') + if get.email.strip() in self.__mail_list: return public.return_msg_gettext(True, 'Email already exists') self.__mail_list.append(get.email.strip()) public.writeFile(self.__mail_list_data, json.dumps(self.__mail_list)) - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') # 添加自定义邮箱地址 def user_mail_send(self, get): if not (hasattr(get, 'email') or hasattr(get, 'stmp_pwd') or hasattr(get, 'hosts') or hasattr(get, 'port')): - return public.returnMsg(False, 'COMPLETE_INFO') + return public.return_msg_gettext(False, 'Please complete the information') # 自定义邮件 self.mail.qq_stmp_insert(get.email.strip(), get.stmp_pwd.strip(), get.hosts.strip(),get.port.strip()) # 测试发送 - if self.mail.qq_smtp_send(get.email.strip(), public.getMsg('TEST_MAIL_TITLE'), public.getMsg('TEST_MAIL_CONTENT')): + if self.mail.qq_smtp_send(get.email.strip(), public.get_msg_gettext('aaPanel Alert Test Email'), public.get_msg_gettext('aaPanel Alert Test Email')): if not get.email.strip() in self.__mail_list: self.__mail_list.append(get.email.strip()) public.writeFile(self.__mail_list_data, json.dumps(self.__mail_list)) - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') else: ret = [] public.writeFile(self.__mail_config, json.dumps(ret)) - return public.returnMsg(False, 'TEST_MAIL_SEND_ERR') + return public.return_msg_gettext(False, 'Email sending failed, please check if the STMP password is correct or the hosts are correct') # 查看自定义邮箱配置 def get_user_mail(self, get): qq_mail_info = json.loads(public.ReadFile(self.__mail_config)) if len(qq_mail_info) == 0: - return public.returnMsg(False, 'NO_DATA') + return public.return_msg_gettext(False, 'No Data') if not 'port' in qq_mail_info:qq_mail_info['port']=465 - return public.returnMsg(True, qq_mail_info) + return public.return_msg_gettext(True, qq_mail_info) #清空数据 def set_empty(self,get): @@ -105,31 +106,31 @@ class config: if type=='dingding': ret = [] public.writeFile(self.__dingding_config, json.dumps(ret)) - return public.returnMsg(True, 'Empty successfully') + return public.return_msg_gettext(True, 'Empty successfully') else: ret = [] public.writeFile(self.__mail_config, json.dumps(ret)) - return public.returnMsg(True, 'Empty successfully') + return public.return_msg_gettext(True, 'Empty successfully') # 用户自定义邮件发送 def user_stmp_mail_send(self, get): - if not (hasattr(get, 'email')): return public.returnMsg(False, 'INPUT_EMAIL') + if not (hasattr(get, 'email')): return public.return_msg_gettext(False, 'Please input your email') emailformat = re.compile(r'[a-zA-Z0-9.-_+%]+@[a-zA-Z0-9]+\.[a-zA-Z0-9]+') - if not emailformat.search(get.email): return public.returnMsg(False, 'EMAIL_ERR') + if not emailformat.search(get.email): return public.return_msg_gettext(False, 'Please enter your vaild email') # 测试发送邮件 - if not get.email.strip() in self.__mail_list: return public.returnMsg(True, 'MAILBOX_NOT_EXIST') - if not (hasattr(get, 'title')): return public.returnMsg(False, 'EMAIL_TITLE') - if not (hasattr(get, 'body')): return public.returnMsg(False, 'EMAIL_CONTENT_ERR') + if not get.email.strip() in self.__mail_list: return public.return_msg_gettext(True, 'The mailbox does not exist, please add it to the mailbox list') + if not (hasattr(get, 'title')): return public.return_msg_gettext(False, 'Please fill in the email title') + if not (hasattr(get, 'body')): return public.return_msg_gettext(False, 'Please enter the email content') # 先判断是否存在stmp信息 qq_mail_info = json.loads(public.ReadFile(self.__mail_config)) if len(qq_mail_info) == 0: - return public.returnMsg(False, 'SMTP_INFO_ERR') + return public.return_msg_gettext(False, 'STMP information was not found, please re-add custom mail STMP information in the settings') if self.mail.qq_smtp_send(get.email.strip(), get.title.strip(), get.body): # 发送成功 - return public.returnMsg(True, 'SEND_SUCCESS') + return public.return_msg_gettext(True, 'Sent successfully') else: - return public.returnMsg(False, 'SEND_FAILED') + return public.return_msg_gettext(False, 'Failed to send') # 查看能使用的告警通道 def get_settings(self, get=None): @@ -159,34 +160,34 @@ class config: # 设置钉钉报警 def set_dingding(self, get): if not (hasattr(get, 'url') or hasattr(get, 'atall')): - return public.returnMsg(False, 'COMPLETE_INFO') + return public.return_msg_gettext(False, 'Please complete the information') if get.atall=='True' or get.atall=='1': get.atall = 'True' else: get.atall = 'False' self.mail.dingding_insert(get.url.strip(), get.atall) - if self.mail.dingding_send(public.getMsg('ALARM_TEST')): - return public.returnMsg(True, 'SET_SUCCESS') + if self.mail.dingding_send(public.get_msg_gettext('aaPanel alarm test')): + return public.return_msg_gettext(True, 'Successfully set') else: ret = [] public.writeFile(self.__dingding_config, json.dumps(ret)) - return public.returnMsg(False, 'MAIL_ADD_FAILED') + return public.return_msg_gettext(False, 'Add failed, please check if the URL is correct') # 查看钉钉 def get_dingding(self, get): qq_mail_info = json.loads(public.ReadFile(self.__dingding_config)) if len(qq_mail_info) == 0: - return public.returnMsg(False, 'NO_DATA') - return public.returnMsg(True, qq_mail_info) + return public.return_msg_gettext(False, 'No Data') + return public.return_msg_gettext(True, qq_mail_info) # 使用钉钉发送消息 def user_dingding_send(self, get): qq_mail_info = json.loads(public.ReadFile(self.__dingding_config)) if len(qq_mail_info) == 0: - return public.returnMsg(False, 'The configuration information of the nails you configured was not found, please add in the settings') - if not (hasattr(get, 'content')): return public.returnMsg(False, 'Please enter the data you need to send') + return public.return_msg_gettext(False, 'The configuration information of the nails you configured was not found, please add in the settings') + if not (hasattr(get, 'content')): return public.return_msg_gettext(False, 'Please enter the data you need to send') if self.mail.dingding_send(get.content): - return public.returnMsg(True, 'SEND_SUCCESS') + return public.return_msg_gettext(True, 'Sent successfully') else: - return public.returnMsg(False, 'SEND_FAILED') + return public.return_msg_gettext(False, 'Failed to send') def getPanelState(self,get): @@ -230,24 +231,181 @@ class config: except: pass + def get_password_safe_file(self): + ''' + @name 获取密码复杂度配置文件 + @auther hwliang<2021-10-18> + @return string + ''' + return public.get_panel_path() + '/data/check_password_safe.pl' + + def check_password_safe(self,password): + ''' + @name 密码复杂度验证 + @auther hwliang<2021-10-18> + @param password(string) 密码 + @return bool + ''' + # 是否检测密码复杂度 + is_check_file = self.get_password_safe_file() + if not os.path.exists(is_check_file): return True + + # 密码长度验证 + if len(password) < 8: return False + + num = 0 + # 密码是否包含数字 + if re.search(r'[0-9]+',password): num += 1 + # 密码是否包含小写字母 + if re.search(r'[a-z]+',password): num += 1 + # 密码是否包含大写字母 + if re.search(r'[A-Z]+',password): num += 1 + # 密码是否包含特殊字符 + if re.search(r'[^\w\s]+',password): num += 1 + # 密码是否包含以上任意3种组合 + if num < 3: return False + return True + + def set_password_safe(self,get): + ''' + @name 设置密码复杂度 + @auther hwliang<2021-10-18> + @param get(string) 参数 + @return dict + ''' + is_check_file = self.get_password_safe_file() + if os.path.exists(is_check_file): + os.remove(is_check_file) + public.WriteLog('TYPE_PANEL','Disable password complexity verification') + return public.returnMsg(True,'Password complexity verification is disabled') + else: + public.writeFile(is_check_file,'True') + public.WriteLog('TYPE_PANEL','Enable password complexity verification') + return public.returnMsg(True,'Password complexity verification has been enabled') + + def get_password_safe(self,get): + ''' + @name 获取密码复杂度 + @auther hwliang<2021-10-18> + @param get(string) 参数 + @return bool + ''' + is_check_file = self.get_password_safe_file() + return os.path.exists(is_check_file) + + + def get_password_expire_file(self): + ''' + @name 获取密码过期配置文件 + @auther hwliang<2021-10-18> + @return string + ''' + return public.get_panel_path() + '/data/password_expire.pl' + + + def set_password_expire(self,get): + ''' + @name 设置密码过期时间 + @auther hwliang<2021-10-18> + @param get{ + expire: int<密码过期时间> 单位:天, + } + @return dict + ''' + expire = int(get.expire) + expire_file = self.get_password_expire_file() + if expire <= 0: + if os.path.exists(expire_file): + os.remove(expire_file) + public.WriteLog('TYPE_PANEL','Disable password expiration authentication') + return public.returnMsg(True,'Password expiration authentication is disabled') + min_expire = 10 + max_expire = 365 * 5 + if expire < min_expire: return public.returnMsg(False,'The password expiration period cannot be less than {} days'.format(min_expire)) + if expire > max_expire: return public.returnMsg(False,'The password expiration period cannot be longer than {} days'.format(max_expire)) + + public.writeFile(self.get_password_expire_file(),str(expire)) + + if expire > 0: + expire_time_file = public.get_panel_path() + '/data/password_expire_time.pl' + public.writeFile(expire_time_file,str(int(time.time()) + (expire * 86400))) + + public.WriteLog('TYPE_PANEL','Set the password expiration time to [{}] days'.format(expire)) + return public.returnMsg(True,'The password expiration time is set to [{}] days'.format(expire)) + + def get_password_config(self,get=None): + ''' + @name 获取密码配置 + @auther hwliang<2021-10-18> + @param get 参数 + @return dict{expire:int,expire_time:int,password_safe:bool} + ''' + expire_file = self.get_password_expire_file() + expire = 0 + expire_time=0 + if os.path.exists(expire_file): + expire = public.readFile(expire_file) + try: + expire = int(expire) + except: + expire = 0 + + # 检查密码过期时间文件是否存在 + expire_time_file = public.get_panel_path() + '/data/password_expire_time.pl' + if not os.path.exists(expire_time_file) and expire > 0: + public.writeFile(expire_time_file,str(int(time.time()) + (expire * 86400))) + + expire_time = public.readFile(expire_time_file) + if expire_time: + expire_time = int(expire_time) + else: + expire_time = 0 + + data = {} + data['expire'] = expire + data['expire_time'] = expire_time + data['password_safe'] = self.get_password_safe(get) + data['ps'] = 'Password expiration configuration is not enabled. For your panel security, please consider enabling it!' + if data['expire_time']: + data['expire_day'] = int((expire_time - time.time()) / 86400) + if data['expire_day'] < 10: + if data['expire_day'] <= 0: + data['ps'] = 'Your password has expired. In case you fail to log in next time, please change your password immediately.' + else: + data['ps'] = "Your panel password will expire in {} days, in order not to affect your normal login, please change the password as soon as possible!".format(data['expire_day']) + else: + data['ps'] = "Your panel password has {} days left to expire!".format(data['expire_day']) + return data def setPassword(self,get): - if get.password1 != get.password2: return public.returnMsg(False,'USER_PASSWORD_CHECK') - if len(get.password1) < 5: return public.returnMsg(False,'USER_PASSWORD_LEN') + get.password1 = public.url_decode(get.password1) + get.password2 = public.url_decode(get.password2) + if get.password1 != get.password2: return public.return_msg_gettext(False,'The passwords entered twice are inconsistent, please try again!') + if len(get.password1) < 5: return public.return_msg_gettext(False,'Password cannot be less than 5 characters!') + if not self.check_password_safe(get.password1): return public.returnMsg(False,'The password must be at least eight characters in length and contain at least three combinations of digits, uppercase letters, lowercase letters, and special characters') public.M('users').where("username=?",(session['username'],)).setField('password',public.password_salt(public.md5(get.password1.strip()),username=session['username'])) - public.WriteLog('TYPE_PANEL','USER_PASSWORD_SUCCESS',(session['username'],)) + public.write_log_gettext('Panel configuration','Successfully modified password for user [{0}]!',(session['username'],)) self.reload_session() - return public.returnMsg(True,'USER_PASSWORD_SUCCESS') + + # 密码过期时间 + expire_time_file = public.get_panel_path() + '/data/password_expire_time.pl' + if os.path.exists(expire_time_file): os.remove(expire_time_file) + self.get_password_config(None) + if session.get('password_expire',False): + session['password_expire'] = False + return public.return_msg_gettext(True,'Setup successfully!') def setUsername(self,get): - if get.username1 != get.username2: return public.returnMsg(False,'USER_USERNAME_CHECK') - if len(get.username1) < 3: return public.returnMsg(False,'USER_USERNAME_LEN') + get.username1 = public.url_decode(get.username1) + get.username2 = public.url_decode(get.username2) + if get.username1 != get.username2: return public.return_msg_gettext(False,'The usernames entered twice are inconsistent, plesea try again!') + if len(get.username1) < 3: return public.return_msg_gettext(False,'Username cannot be less than 3 characters') public.M('users').where("username=?",(session['username'],)).setField('username',get.username1.strip()) - public.WriteLog('TYPE_PANEL','USER_USERNAME_SUCCESS',(session['username'],get.username2)) + public.write_log_gettext('Panel configuration','Username is modified from [{}] to [{}]',(session['username'],get.username2)) session['username'] = get.username1 self.reload_session() - return public.returnMsg(True,'USER_USERNAME_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #取用户列表 def get_users(self,args): @@ -256,79 +414,88 @@ class config: # 创建新用户 def create_user(self,args): - if session['uid'] != 1: return public.returnMsg(False,'PERMISSION_DENIED') - if len(args.username) < 2: return public.returnMsg(False,'USERNAME_ERR') - if len(args.password) < 8: return public.returnMsg(False,'PASSWORD_ERR') + args.username = public.url_decode(args.username) + args.password = public.url_decode(args.password) + if session['uid'] != 1: return public.return_msg_gettext(False,'Permission denied!') + if len(args.username) < 2: return public.return_msg_gettext(False,'User name must be at least 2 characters') + if len(args.password) < 8: return public.return_msg_gettext(False,'Password must be at least 8 characters') pdata = { "username": args.username.strip(), "password": public.password_salt(public.md5(args.password.strip()),username=args.username.strip()) } if(public.M('users').where('username=?',(pdata['username'],)).count()): - return public.returnMsg(False,'USERNAME_EXIST') + return public.return_msg_gettext(False,'The specified username already exists!') if(public.M('users').insert(pdata)): - public.WriteLog('USER_MANAGE','CREATE_USER',(pdata['username'],)) - return public.returnMsg(True,'CREATE_USER_SUCCESS',(pdata['username'],)) - return public.returnMsg(False,'CREATE_USER_FAILED') + public.write_log_gettext('User Management','Create new user {}',(pdata['username'],)) + return public.return_msg_gettext(True,'Create new user {} success!',(pdata['username'],)) + return public.return_msg_gettext(False,'Create new user failed!') # 删除用户 def remove_user(self,args): - if session['uid'] != 1: return public.returnMsg(False,'PERMISSION_DENIED') - if int(args.id) == 1: return public.returnMsg(False,'DEL_USER_ERR') + if session['uid'] != 1: return public.return_msg_gettext(False,'Permission denied!') + if int(args.id) == 1: return public.return_msg_gettext(False,'Cannot delete initial default user!') username = public.M('users').where('id=?',(args.id,)).getField('username') - if not username: return public.returnMsg(False,'USERNAME_NOT_EXIST') + if not username: return public.return_msg_gettext(False,'The specified username not exists!') if(public.M('users').where('id=?',(args.id,)).delete()): - public.WriteLog('USER_MANAGE','DEL_USER',(username)) - return public.returnMsg(True,'DEL_USER_SUCCESS',(username,)) - return public.returnMsg(False,'DEL_USER_FAILED') + public.write_log_gettext('User Management','Delete users [{}]',(username)) + return public.return_msg_gettext(True,'Delete user {} success!',(username,)) + return public.return_msg_gettext(False,'User deletion failed!') # 修改用户 def modify_user(self,args): - if session['uid'] != 1: return public.returnMsg(False,'PERMISSION_DENIED') + if session['uid'] != 1: return public.return_msg_gettext(False,'Permission denied!') username = public.M('users').where('id=?',(args.id,)).getField('username') pdata = {} if 'username' in args: - if len(args.username) < 2: return public.returnMsg(False,'USERNAME_ERR') + args.username = public.url_decode(args.username) + if len(args.username) < 2: return public.return_msg_gettext(False,'User name must be at least 2 characters') pdata['username'] = args.username.strip() if 'password' in args: if args.password: - if len(args.password) < 8: return public.returnMsg(False,'PASSWORD_ERR') + args.password = public.url_decode(args.password) + if len(args.password) < 8: return public.return_msg_gettext(False,'Password must be at least 8 characters') pdata['password'] = public.password_salt(public.md5(args.password.strip()),username=username) if(public.M('users').where('id=?',(args.id,)).update(pdata)): - public.WriteLog('USER_MANAGE',"EDIT_USER",(username,)) - return public.returnMsg(True,'EDIT_SUCCESS') - return public.returnMsg(False,'NO_CHANGE_SUBMITTED') + public.write_log_gettext('User Management',"Edit user {}",(username,)) + return public.return_msg_gettext(True,'Setup successfully!') + return public.return_msg_gettext(False,'No changes submitted') def setPanel(self,get): - if not public.IsRestart(): return public.returnMsg(False,'EXEC_ERR_TASK') + if not public.IsRestart(): return public.return_msg_gettext(False,'Please run the program when all install tasks finished!') if 'limitip' in get: if get.limitip.find('/') != -1: - return public.returnMsg(False,'The authorized IP format is incorrect, and the subnet segment writing is not supported') + return public.return_msg_gettext(False,'The authorized IP format is incorrect, and the subnet segment writing is not supported') isReWeb = False sess_out_path = 'data/session_timeout.pl' if 'session_timeout' in get: - session_timeout = int(get.session_timeout) + try: + session_timeout = int(get.session_timeout) + except: + return public.returnMsg(False,"Timeout must be an integer!") s_time_tmp = public.readFile(sess_out_path) if not s_time_tmp: s_time_tmp = '0' if int(s_time_tmp) != session_timeout: - if session_timeout < 300: return public.returnMsg(False,'NOT_LESS_THAN_TIMEOUT') + if session_timeout < 300 or session_timeout > 86400: return public.return_msg_gettext(False,'The timeout time needs to be between 300-86400') public.writeFile(sess_out_path,str(session_timeout)) isReWeb = True + else: + return public.returnMsg(False,'Timeout must be an integer!') workers_p = 'data/workers.pl' if 'workers' in get: workers = int(get.workers) if int(public.readFile(workers_p)) != workers: - if workers < 1 or workers > 1024: return public.returnMsg(False,public.GetMsg("PANEL_THREAD_RANGE_ERR")) + if workers < 1 or workers > 1024: return public.return_msg_gettext(False,public.get_msg_gettext('The number of panel threads should be between 1-1024')) public.writeFile(workers_p,str(workers)) isReWeb = True if get.domain: reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$" - if not re.match(reg, get.domain): return public.returnMsg(False,'SITE_ADD_ERR_DOMAIN') + if not re.match(reg, get.domain): return public.return_msg_gettext(False,'Format of primary domain is incorrect') oldPort = public.GetHost(True) if not 'port' in get: get.port = oldPort @@ -336,11 +503,11 @@ class config: if oldPort != get.port: get.port = str(int(get.port)) if self.IsOpen(get.port): - return public.returnMsg(False,'PORT_CHECK_EXISTS',(get.port,)) - if int(get.port) >= 65535 or int(get.port) < 100: return public.returnMsg(False,'PORT_CHECK_RANGE') + return public.return_msg_gettext(False,'Port [{}] is in use!',(get.port,)) + if int(get.port) >= 65535 or int(get.port) < 100: return public.return_msg_gettext(False,'Port range is incorrect! should be between 100-65535') public.writeFile('data/port.pl',get.port) import firewalls - get.ps = public.getMsg('PORT_CHECK_PS') + get.ps = public.get_msg_gettext('New panel port') fw = firewalls.firewalls() fw.AddAcceptPort(get) get.port = oldPort @@ -349,8 +516,8 @@ class config: isReWeb = True if get.webname != session['title']: - session['title'] = public.xssencode(get.webname) - public.SetConfigValue('title',public.xssencode(get.webname)) + session['title'] = public.xssencode2(get.webname) + public.SetConfigValue('title',public.xssencode2(get.webname)) limitip = public.readFile('data/limitip.conf') if get.limitip != limitip: @@ -360,7 +527,10 @@ class config: public.writeFile('data/domain.conf',public.xssencode2(get.domain).strip()) public.writeFile('data/iplist.txt',get.address) - + import files + fs = files.files() + if not fs.CheckDir(get.backup_path): return public.returnMsg(False,'Cannot use system critical directory as default backup directory') + if not fs.CheckDir(get.sites_path): return public.returnMsg(False,'Cannot use system critical directory as default site directory') public.M('config').where("id=?",('1',)).save('backup_path,sites_path',(get.backup_path,get.sites_path)) session['config']['backup_path'] = os.path.join('/',get.backup_path) session['config']['sites_path'] = os.path.join('/',get.sites_path) @@ -379,27 +549,18 @@ class config: mhost = public.GetHost() if get.domain.strip(): mhost = get.domain - data = {'uri':request.path,'host':mhost+':'+newPort,'status':True,'isReWeb':isReWeb,'msg':public.getMsg('PANEL_SAVE')} - public.WriteLog('TYPE_PANEL','PANEL_SET_SUCCESS',(newPort,get.domain,get.backup_path,get.sites_path,get.address,get.limitip)) + data = {'uri':request.path,'host':mhost+':'+newPort,'status':True,'isReWeb':isReWeb,'msg':public.get_msg_gettext('Saved')} + public.write_log_gettext('Panel configuration','Set panel port [{}], domain [{}], default backup directory [{}], default site directory [{}], server IP [{}], authorized IP [{}]!',(newPort,get.domain,get.backup_path,get.sites_path,get.address,get.limitip)) if isReWeb: public.restart_panel() return data def set_admin_path(self,get): get.admin_path = get.admin_path.strip() - if get.admin_path == '': get.admin_path = '/' - if get.admin_path != '/': - if len(get.admin_path) < 6: return public.returnMsg(False, 'SECURITY_ENTRANCE_ADDRESS_NOT_LESS_THAN') - if get.admin_path in admin_path_checks: return public.returnMsg(False, 'SECURITY_ENTRANCE_ADDRESS_EXIST') - if not public.path_safe_check(get.admin_path) or get.admin_path[-1] == '.': return public.returnMsg(False, 'SECURITY_ENTRANCE_ADDRESS_INCORRECT') - if get.admin_path[0] != '/': return public.returnMsg(False, 'SECURITY_ENTRANCE_ADDRESS_INCORRECT') - else: - get.domain = public.readFile('data/domain.conf') - if not get.domain: get.domain = '' - get.limitip = public.readFile('data/limitip.conf') - if not get.limitip: get.limitip = '' - if not get.domain.strip() and not get.limitip.strip() and not os.path.exists('config/basic_auth.json'): - return public.returnMsg(False,'SECURITY_ENTRANCE_ADDRESS_TRUEN_OFF_WARN') + if len(get.admin_path) < 6: return public.return_msg_gettext(False,'Security Entrance cannot be less than 6 characters!') + if get.admin_path in admin_path_checks: return public.return_msg_gettext(False,'This entrance has been used by the panel, please set another entrances!') + if not public.path_safe_check(get.admin_path) or get.admin_path[-1] == '.': return public.returnMsg(False,'Entrance address format is incorrect, e.g. /my_panel') + if get.admin_path[0] != '/': return public.return_msg_gettext(False,'Entrance address format is incorrect, e.g. /my_panel') admin_path_file = 'data/admin_path.pl' admin_path = '/' @@ -407,7 +568,7 @@ class config: if get.admin_path != admin_path: public.writeFile(admin_path_file,get.admin_path) public.restart_panel() - return public.returnMsg(True, 'EDIT_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') def setPathInfo(self,get): @@ -432,16 +593,16 @@ class config: if type == 'on':status = '1' conf = re.sub(rep,"\ncgi.fix_pathinfo = "+status+"\n",conf) public.writeFile(path,conf) - public.WriteLog("TYPE_PHP", "PHP_PATHINFO_SUCCESS",(version,type)) + public.write_log_gettext("PHP configuration", "Set PATH_INFO module to [{}] for PHP-{}!",(version,type)) public.phpReload(version) - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #设置文件上传大小限制 def setPHPMaxSize(self,get): version = get.version max = get.max - if int(max) < 2: return public.returnMsg(False,'PHP_UPLOAD_MAX_ERR') + if int(max) < 2: return public.return_msg_gettext(False,'Limit of upload size cannot be less than 2 MB') #设置PHP path = public.GetConfigValue('setup_path')+'/php/'+version+'/etc/php.ini' ols_php_path = '/usr/local/lsws/lsphp{}/etc/php/{}.{}/litespeed/php.ini'.format(get.version, get.version[0],get.version[1]) @@ -453,9 +614,9 @@ class config: if not os.path.exists(p): continue conf = public.readFile(p) - rep = r"\nupload_max_filesize\s*=\s*[0-9]+M" + rep = r"\nupload_max_filesize\s*=\s*[0-9]+M?m?" conf = re.sub(rep,r'\nupload_max_filesize = '+max+'M',conf) - rep = r"\npost_max_size\s*=\s*[0-9]+M" + rep = r"\npost_max_size\s*=\s*[0-9]+M?m?" conf = re.sub(rep,r'\npost_max_size = '+max+'M',conf) public.writeFile(p,conf) @@ -463,7 +624,7 @@ class config: #设置Nginx path = public.GetConfigValue('setup_path')+'/nginx/conf/nginx.conf' conf = public.readFile(path) - rep = r"client_max_body_size\s+([0-9]+)m" + rep = r"client_max_body_size\s+([0-9]+)m?M?" tmp = re.search(rep,conf).groups() if int(tmp[0]) < int(max): conf = re.sub(rep,'client_max_body_size '+max+'m',conf) @@ -471,8 +632,8 @@ class config: public.serviceReload() public.phpReload(version) - public.WriteLog("TYPE_PHP", "PHP_UPLOAD_MAX",(version,max)) - return public.returnMsg(True,'SET_SUCCESS') + public.write_log_gettext("PHP configuration", "Set max upload size to [{} MB] for PHP-{}!",(version,max)) + return public.return_msg_gettext(True,'Setup successfully!') #设置禁用函数 def setPHPDisable(self,get): @@ -480,24 +641,24 @@ class config: ols_php_path = '/usr/local/lsws/lsphp{}/etc/php/{}.{}/litespeed/php.ini'.format(get.version, get.version[0],get.version[1]) if os.path.exists('/etc/redhat-release'): ols_php_path = '/usr/local/lsws/lsphp' + get.version + '/etc/php.ini' - if not os.path.exists(filename): return public.returnMsg(False,'PHP_NOT_EXISTS') + if not os.path.exists(filename): return public.return_msg_gettext(False,'Requested PHP version does NOT exist!') for file in [filename,ols_php_path]: if not os.path.exists(file): continue phpini = public.readFile(file) rep = r"disable_functions\s*=\s*.*\n" phpini = re.sub(rep, 'disable_functions = ' + get.disable_functions + "\n", phpini) - public.WriteLog('TYPE_PHP','PHP_DISABLE_FUNCTION',(get.version,get.disable_functions)) + public.write_log_gettext('PHP configuration','Modified disabled function to [{}] for PHP-{}',(get.version,get.disable_functions)) public.writeFile(file,phpini) public.phpReload(get.version) public.serviceReload() - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #设置PHP超时时间 def setPHPMaxTime(self,get): time = get.time version = get.version - if int(time) < 30 or int(time) > 86400: return public.returnMsg(False,'PHP_TIMEOUT_ERR') + if int(time) < 30 or int(time) > 86400: return public.return_msg_gettext(False,'Please fill in the value between 30 and 86400!') file = public.GetConfigValue('setup_path')+'/php/'+version+'/etc/php-fpm.conf' conf = public.readFile(file) rep = r"request_terminate_timeout\s*=\s*([0-9]+)\n" @@ -526,10 +687,10 @@ class config: conf = re.sub(rep,'fastcgi_read_timeout '+time+';',conf) public.writeFile(path,conf) - public.WriteLog("TYPE_PHP", "PHP_TIMEOUT",(version,time)) + public.write_log_gettext("PHP configuration", "Set maximum time of script to [{} second] for PHP-{}!",(version,time)) public.serviceReload() public.phpReload(version) - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') #取FPM设置 @@ -585,7 +746,7 @@ class config: max_spare_servers = get.max_spare_servers pm = get.pm if not pm in ['static','dynamic','ondemand']: - return public.returnMsg(False,'WRONG_MODE') + return public.return_msg_gettext(False,'Wrong operating mode') file = public.GetConfigValue('setup_path')+"/php/"+version+"/etc/php-fpm.conf" conf = public.readFile(file) @@ -632,8 +793,8 @@ class config: public.writeFile(file,conf) public.phpReload(version) public.sync_php_address(version) - public.WriteLog("TYPE_PHP",'PHP_CHILDREN', (version,max_children,start_servers,min_spare_servers,max_spare_servers)) - return public.returnMsg(True, 'SET_SUCCESS') + public.write_log_gettext("PHP configuration",'Set concurrency of PHP-{}, max_children={}, start_servers={}, min_spare_servers={}, max_spare_servers={}', (version,max_children,start_servers,min_spare_servers,max_spare_servers)) + return public.return_msg_gettext(True, 'Setup successfully!') #同步时间 def syncDate(self,get): @@ -642,8 +803,8 @@ class config: time_arr = time.localtime(new_time) date_str = time.strftime("%Y-%m-%d %H:%M:%S", time_arr) public.ExecShell('date -s "%s"' % date_str) - public.WriteLog("TYPE_PANEL", "DATE_SUCCESS") - return public.returnMsg(True,"DATE_SUCCESS") + public.write_log_gettext("Panel configuration", 'Update Succeeded!') + return public.return_msg_gettext(True,'Setup successfully!') def IsOpen(self,port): #检查端口是否占用 @@ -662,25 +823,25 @@ class config: if hasattr(get,'day'): get.day = int(get.day) get.day = str(get.day) - if(get.day < 1): return public.returnMsg(False,"CONTROL_ERR") + if(get.day < 1): return public.return_msg_gettext(False,'Number of saving days is illegal!') except: pass filename = 'data/control.conf' if get.type == '1': public.writeFile(filename,get.day) - public.WriteLog("TYPE_PANEL",'CONTROL_OPEN',(get.day,)) + public.write_log_gettext("Panel configuration",'Turned on monitory service, save for [{}] day!',(get.day,)) elif get.type == '0': if os.path.exists(filename): os.remove(filename) - public.WriteLog("TYPE_PANEL", "CONTROL_CLOSE") + public.write_log_gettext("Panel configuration", 'Monitor service turned off!') elif get.type == 'del': - if not public.IsRestart(): return public.returnMsg(False,'EXEC_ERR_TASK') + if not public.IsRestart(): return public.return_msg_gettext(False,'Please run the program when all install tasks finished!') os.remove("data/system.db") import db sql = db.Sql() sql.dbfile('system').create('system') - public.WriteLog("TYPE_PANEL", "CONTROL_CLOSE") - return public.returnMsg(True,"CONTROL_CLOSE") + public.write_log_gettext("Panel configuration", 'Monitor service turned off!') + return public.return_msg_gettext(True,'Setup successfully!') else: data = {} @@ -695,18 +856,18 @@ class config: data['status'] = False return data - return public.returnMsg(True,"SET_SUCCESS") + return public.return_msg_gettext(True,'Successfully set') #关闭面板 def ClosePanel(self,get): filename = 'data/close.pl' if os.path.exists(filename): os.remove(filename) - return public.returnMsg(True, 'OPEN_SUCCESSFUL') + return public.returnMsg(True, 'Setup successfully!') public.writeFile(filename, 'True') public.ExecShell("chmod 600 " + filename) public.ExecShell("chown root.root " + filename) - return public.returnMsg(True,'PANEL_CLOSE') + return public.return_msg_gettext(True,'Setup successfully!') #设置自动更新 @@ -719,7 +880,7 @@ class config: public.writeFile(filename,'True') public.ExecShell("chmod 600 " + filename) public.ExecShell("chown root.root " + filename) - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #设置二级密码 def SetPanelLock(self,get): @@ -745,62 +906,134 @@ class config: else: public.writeFile(filename,'True') - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #设置模板 def SetTemplates(self,get): public.writeFile('data/templates.pl',get.templates) - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #设置面板SSL - def SetPanelSSL(self,get): - if hasattr(get,"email"): - #rep_mail = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$" - rep_mail = r"[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?" - if not re.search(rep_mail,get.email): - return public.returnMsg(False,'EMAIL_FORMAT_ERR') + def SetPanelSSL(self, get): + if hasattr(get, "cert_type") and str(get.cert_type) == "2": + # rep_mail = r"[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?" + # if not re.search(rep_mail,get.email): + # return public.return_msg_gettext(False,'The E-Mail format is illegal') import setPanelLets sp = setPanelLets.setPanelLets() sps = sp.set_lets(get) return sps else: - sslConf = self._setup_path+'/data/ssl.pl' + sslConf = self._setup_path + '/data/ssl.pl' if os.path.exists(sslConf): - public.ExecShell('rm -f ' + sslConf) + public.ExecShell('rm -f ' + sslConf + '&& rm -f /www/server/panel/ssl/*') g.rm_ssl = True - return public.returnMsg(True,'PANEL_SSL_CLOSE') + return public.return_msg_gettext(True, 'SSL turned off,Please use http protocol to access the panel!') else: - public.ExecShell('pip install cffi') - public.ExecShell('pip install cryptography') - public.ExecShell('pip install pyOpenSSL') + public.ExecShell('btpip install cffi') + public.ExecShell('btpip install cryptography') + public.ExecShell('btpip install pyOpenSSL') try: - if not self.CreateSSL(): return public.returnMsg(False,'PANEL_SSL_ERR') - public.writeFile(sslConf,'True') + if not self.CreateSSL(): + return public.return_msg_gettext(False, + 'Error, unable to auto install pyOpenSSL!

                                      Plesea try to manually install: pip install pyOpenSSL

                                      ') + public.writeFile(sslConf, 'True') except: - return public.returnMsg(False,'PANEL_SSL_ERR') - return public.returnMsg(True,'PANEL_SSL_OPEN') + return public.return_msg_gettext(False, + 'Error, unable to auto install pyOpenSSL!

                                      Plesea try to manually install: pip install pyOpenSSL

                                      ') + return public.return_msg_gettext(True, + 'SSL is turned on, plesea use https protocol to access the panel!') #自签证书 + # def CreateSSL(self): + # if os.path.exists('ssl/input.pl'): return True + # import OpenSSL + # key = OpenSSL.crypto.PKey() + # key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) + # cert = OpenSSL.crypto.X509() + # cert.set_serial_number(0) + # cert.get_subject().CN = public.GetLocalIp() + # cert.set_issuer(cert.get_subject()) + # cert.gmtime_adj_notBefore( 0 ) + # cert.gmtime_adj_notAfter(86400 * 3650) + # cert.set_pubkey( key ) + # cert.sign( key, 'md5' ) + # cert_ca = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) + # private_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) + # if len(cert_ca) > 100 and len(private_key) > 100: + # public.writeFile('ssl/certificate.pem',cert_ca,'wb+') + # public.writeFile('ssl/privateKey.pem',private_key,'wb+') + # return True + # return False + # 自签证书 + def CreateSSL(self): - if os.path.exists('ssl/input.pl'): return True - import OpenSSL - key = OpenSSL.crypto.PKey() - key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) - cert = OpenSSL.crypto.X509() - cert.set_serial_number(0) - cert.get_subject().CN = public.GetLocalIp() - cert.set_issuer(cert.get_subject()) - cert.gmtime_adj_notBefore( 0 ) - cert.gmtime_adj_notAfter(86400 * 3650) - cert.set_pubkey( key ) - cert.sign( key, 'md5' ) - cert_ca = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) - private_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) - if len(cert_ca) > 100 and len(private_key) > 100: - public.writeFile('ssl/certificate.pem',cert_ca,'wb+') - public.writeFile('ssl/privateKey.pem',private_key,'wb+') - return True + import base64 + userInfo = public.get_user_info() + if not userInfo: + userInfo['uid'] = 0 + userInfo['access_key'] = 'B' * 32 + domains = self.get_host_all() + pdata = { + "action": "get_domain_cert", + "company": "aapanel.com", + "domain": ','.join(domains), + "uid": userInfo['uid'], + "access_key": 'B' * 32, + "panel": 1 + } + cert_api = 'https://api.aapanel.com/aapanel_cert' + result = json.loads(public.httpPost(cert_api, {'data': json.dumps(pdata)})) + if 'status' in result: + if result['status']: + # if os.path.exists('ssl/certificate.pem'): + # os.remove('ssl/certificate.pem') + # if os.path.exists('ssl/privateKey.pem'): + # os.remove('ssl/privateKey.pem') + # if os.path.exists('ssl/baota_root.pfx'): + # os.remove('ssl/baota_root.pfx') + # if os.path.exists('ssl/root_password.pl'): + # os.remove('ssl/root_password.pl') + public.writeFile('ssl/certificate.pem', result['cert']) + public.writeFile('ssl/privateKey.pem', result['key']) + public.writeFile('ssl/baota_root.pfx', base64.b64decode(result['pfx']), 'wb+') + public.writeFile('ssl/root_password.pl', result['password']) + public.writeFile('data/ssl.pl', 'True') + # public.ExecShell("/etc/init.d/bt reload") + print('1') + return True + print('0') return False + def get_ipaddress(self): + ''' + @name 获取本机IP地址 + @author hwliang<2020-11-24> + @return list + ''' + ipa_tmp = \ + public.ExecShell("ip a |grep inet|grep -v inet6|grep -v 127.0.0.1|awk '{print $2}'|sed 's#/[0-9]*##g'")[ + 0].strip() + iplist = ipa_tmp.split('\n') + return iplist + + def get_host_all(self): + local_ip = ['127.0.0.1', '::1', 'localhost'] + ip_list = [] + bind_ip = self.get_ipaddress() + + for ip in bind_ip: + ip = ip.strip() + if ip in local_ip: continue + if ip in ip_list: continue + ip_list.append(ip) + net_ip = public.httpGet("https://www.aapanel.com/api/common/getClientIP") + + if net_ip: + net_ip = net_ip.strip() + if not net_ip in ip_list: + ip_list.append(net_ip) + ip_list = [ip_list[-1], ip_list[0]] + return ip_list #生成Token def SetToken(self,get): data = {} @@ -830,30 +1063,30 @@ class config: #校验是还是重复 isAdd = public.M('panel').where('title=? OR url=?',(get.title,get.url)).count() - if isAdd: return public.returnMsg(False,'PANEL_SSL_ADD_EXISTS') + if isAdd: return public.return_msg_gettext(False,'Notes or panel address duplicate!') import time,json isRe = public.M('panel').add('title,url,username,password,click,addtime',(public.xssencode2(get.title),public.xssencode2(get.url),public.xssencode2(get.username),get.password,0,int(time.time()))) - if isRe: return public.returnMsg(True,'ADD_SUCCESS') - return public.returnMsg(False,'ADD_ERROR') + if isRe: return public.return_msg_gettext(True,'Setup successfully!') + return public.return_msg_gettext(False,'Failed to add') #修改面板资料 def SetPanelInfo(self,get): #校验是还是重复 isSave = public.M('panel').where('(title=? OR url=?) AND id!=?',(get.title,get.url,get.id)).count() - if isSave: return public.returnMsg(False,'PANEL_SSL_ADD_EXISTS') + if isSave: return public.return_msg_gettext(False,'Notes or panel address duplicate!') import time,json #更新到数据库 isRe = public.M('panel').where('id=?',(get.id,)).save('title,url,username,password',(get.title,get.url,get.username,get.password)) - if isRe: return public.returnMsg(True,'EDIT_SUCCESS') - return public.returnMsg(False,'EDIT_ERROR') + if isRe: return public.return_msg_gettext(True,'Setup successfully!') + return public.return_msg_gettext(False,'Failed to modify') #删除面板资料 def DelPanelInfo(self,get): isExists = public.M('panel').where('id=?',(get.id,)).count() - if not isExists: return public.returnMsg(False,'PANEL_SSL_ADD_NOT_EXISTS') + if not isExists: return public.return_msg_gettext(False,'Requested panel info does NOT exist!') public.M('panel').where('id=?',(get.id,)).delete() - return public.returnMsg(True,'DEL_SUCCESS') + return public.return_msg_gettext(True,'Successfully deleted') #点击计数 def ClickPanelInfo(self,get): @@ -864,20 +1097,20 @@ class config: #获取PHP配置参数 def GetPHPConf(self,get): gets = [ - {'name':'short_open_tag','type':1,'ps':public.getMsg('PHP_CONF_1')}, - {'name':'asp_tags','type':1,'ps':public.getMsg('PHP_CONF_2')}, - {'name':'max_execution_time','type':2,'ps':public.getMsg('PHP_CONF_4')}, - {'name':'max_input_time','type':2,'ps':public.getMsg('PHP_CONF_5')}, - {'name':'memory_limit','type':2,'ps':public.getMsg('PHP_CONF_6')}, - {'name':'post_max_size','type':2,'ps':public.getMsg('PHP_CONF_7')}, - {'name':'file_uploads','type':1,'ps':public.getMsg('PHP_CONF_8')}, - {'name':'upload_max_filesize','type':2,'ps':public.getMsg('PHP_CONF_9')}, - {'name':'max_file_uploads','type':2,'ps':public.getMsg('PHP_CONF_10')}, - {'name':'default_socket_timeout','type':2,'ps':public.getMsg('PHP_CONF_11')}, - {'name':'error_reporting','type':3,'ps':public.getMsg('PHP_CONF_12')}, - {'name':'display_errors','type':1,'ps':public.getMsg('PHP_CONF_13')}, - {'name':'cgi.fix_pathinfo','type':0,'ps':public.getMsg('PHP_CONF_14')}, - {'name':'date.timezone','type':3,'ps':public.getMsg('PHP_CONF_15')} + {'name':'short_open_tag','type':1,'ps':public.get_msg_gettext('Short tag support')}, + {'name':'asp_tags','type':1,'ps':public.get_msg_gettext('ASP tag support')}, + {'name':'max_execution_time','type':2,'ps':public.get_msg_gettext('Max time of running script')}, + {'name':'max_input_time','type':2,'ps':public.get_msg_gettext('Max time of input')}, + {'name':'memory_limit','type':2,'ps':public.get_msg_gettext('Limit of script memory')}, + {'name':'post_max_size','type':2,'ps':public.get_msg_gettext('Max size of POST data')}, + {'name':'file_uploads','type':1,'ps':public.get_msg_gettext('Whether to allow upload file')}, + {'name':'upload_max_filesize','type':2,'ps':public.get_msg_gettext('Max size of upload file')}, + {'name':'max_file_uploads','type':2,'ps':public.get_msg_gettext('Max value of simultaneously upload file')}, + {'name':'default_socket_timeout','type':2,'ps':public.get_msg_gettext('Socket over time')}, + {'name':'error_reporting','type':3,'ps':public.get_msg_gettext('Level of error')}, + {'name':'display_errors','type':1,'ps':public.get_msg_gettext('Whether to output detailed error info')}, + {'name':'cgi.fix_pathinfo','type':0,'ps':public.get_msg_gettext('Whether to turn on pathinfo')}, + {'name':'date.timezone','type':3,'ps':public.get_msg_gettext('Timezone')} ] phpini_file = '/www/server/php/' + get.version + '/etc/php.ini' if public.get_webserver() == 'openlitespeed': @@ -885,6 +1118,8 @@ class config: if os.path.exists('/etc/redhat-release'): phpini_file = '/usr/local/lsws/lsphp' + get.version + '/etc/php.ini' phpini = public.readFile(phpini_file) + if not phpini: + return public.return_msg_gettext(False,"Error reading PHP configuration file, please try to reinstall this PHP!") result = [] for g in gets: rep = g['name'] + r'\s*=\s*([0-9A-Za-z_&/ ~]+)(\s*;?|\r?\n)' @@ -959,7 +1194,7 @@ class config: public.writeFile(p,phpini) public.ExecShell(reload_str) public.ExecShell(reload_ols_str) - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') # 取Session缓存方式 @@ -1007,16 +1242,16 @@ class config: if g != "files": iprep = r"(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})" if not re.search(iprep, ip): - return public.returnMsg(False, 'FIREWALL_IP_FORMAT') + return public.return_msg_gettext(False, 'IP address is illegal!') try: port = int(port) if port >= 65535 or port < 1: - return public.returnMsg(False, 'SITE_ADD_ERR_PORT') + return public.return_msg_gettext(False, 'Port range is incorrect! should be between 100-65535') except: - return public.returnMsg(False, 'SITE_ADD_ERR_PORT') + return public.return_msg_gettext(False, 'Port range is incorrect! should be between 100-65535') prep = r"[\~\`\/\=]" if re.search(prep, passwd): - return public.returnMsg(False, 'SPECIAL_CHARACTRES', ('" ~ ` / = "')) + return public.return_msg_gettext(False, 'Please do NOT enter the following special characters {}', ('" ~ ` / = "')) filename = '/www/server/php/' + get.version + '/etc/php.ini' filename_ols = None if os.path.exists("/usr/local/lsws/bin/lswsctrl"): @@ -1042,7 +1277,7 @@ class config: phpini = re.sub(rep, val, phpini) if g == "memcached": if not re.search("memcached.so", phpini) and "memcached.so" not in ols_so_list: - return public.returnMsg(False, 'INSTALL_EXTEND_FIRST', (g,)) + return public.return_msg_gettext(False, 'Please install the {} extension first.', (g,)) rep = r'\nsession.save_path\s*=\s*(.+)\r?\n' val = r'\nsession.save_path = "%s:%s" \n' % (ip,port) if re.search(rep, phpini): @@ -1051,7 +1286,7 @@ class config: phpini = re.sub('\n;session.save_path = "/tmp"', '\n;session.save_path = "/tmp"' + val, phpini) if g == "memcache": if not re.search("memcache.so", phpini) and "memcache.so" not in ols_so_list: - return public.returnMsg(False, 'INSTALL_EXTEND_FIRST', (g,)) + return public.return_msg_gettext(False, 'Please install the {} extension first.', (g,)) rep = r'\nsession.save_path\s*=\s*(.+)\r?\n' val = r'\nsession.save_path = "tcp://%s:%s"\n' % (ip, port) if re.search(rep, phpini): @@ -1060,7 +1295,7 @@ class config: phpini = re.sub('\n;session.save_path = "/tmp"', '\n;session.save_path = "/tmp"' + val, phpini) if g == "redis": if not re.search("redis.so", phpini) and "redis.so" not in ols_so_list: - return public.returnMsg(False, 'INSTALL_EXTEND_FIRST', (g,)) + return public.return_msg_gettext(False, 'Please install the {} extension first.', (g,)) if passwd: passwd = "?auth=" + passwd else: @@ -1082,7 +1317,7 @@ class config: public.writeFile(f, phpini) public.ExecShell('/etc/init.d/php-fpm-' + get.version + ' reload') public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') # 获取Session文件数量 def GetSessionCount(self, get): @@ -1120,9 +1355,9 @@ class config: # old_file_conf = int(public.ExecShell(s)[0].split("\n")[0]) old_file_conf = self.GetSessionCount(get)["oldfile"] if old_file_conf == 0: - return public.returnMsg(True, 'DEL_SUCCESS') + return public.return_msg_gettext(True, 'Successfully deleted') else: - return public.returnMsg(True, 'DEL_ERROR') + return public.return_msg_gettext(True, 'Failed to delete') # 获取面板证书 def GetPanelSSL(self, get): @@ -1142,9 +1377,9 @@ class config: public.writeFile(keyPath,get.privateKey) if get.certPem: public.writeFile(certPath, get.certPem) - if not public.CheckCert(checkCert): return public.returnMsg(False, 'SITE_SSL_ERR_CERT') + if not public.CheckCert(checkCert): return public.return_msg_gettext(False, 'Certificate ERROR, please check!') public.writeFile('ssl/input.pl', 'True') - return public.returnMsg(True, 'SITE_SSL_SUCCESS') + return public.return_msg_gettext(True, 'Certificate saved!') #获取配置 def get_config(self,get): @@ -1238,19 +1473,19 @@ class config: ipv6_file = 'data/ipv6.pl' if self.get_ipv6_listen(get): os.remove(ipv6_file) - public.WriteLog('TYPE_CONFIG', 'P_STOP_IPV6!') + public.write_log_gettext('Panel setting', 'Disable IPv6 compatibility of the panel!') else: public.writeFile(ipv6_file, 'True') - public.WriteLog('TYPE_CONFIG', 'P_START_IPV6!') + public.write_log_gettext('Panel setting', 'Enable IPv6 compatibility of the panel!') public.restart_panel() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') #自动补充CLI模式下的PHP版本 def auto_cli_php_version(self,get): import panelSite php_versions = panelSite.panelSite().GetPHPVersion(get) php_bin_src = "/www/server/php/%s/bin/php" % php_versions[-1]['version'] - if not os.path.exists(php_bin_src): return public.returnMsg(False,'PHP_NOT_INSTALL') + if not os.path.exists(php_bin_src): return public.return_msg_gettext(False,'PHP is not installed') get.php_version = php_versions[-1]['version'] self.set_cli_php_version(get) return php_versions[-1] @@ -1264,7 +1499,7 @@ class config: import panelSite php_versions = panelSite.panelSite().GetPHPVersion(get) if len(php_versions)==0: - return public.returnMsg(False,'GET_PHP_VER_ERR') + return public.return_msg_gettext(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} @@ -1282,7 +1517,7 @@ class config: php_pecl_src = "/www/server/php/%s/bin/pecl" % get.php_version php_pear = '/usr/bin/pear' php_pear_src = "/www/server/php/%s/bin/pear" % get.php_version - if not os.path.exists(php_bin_src): return public.returnMsg(False,'SPECIFIED_PHP_NOT_INSTALL') + if not os.path.exists(php_bin_src): return public.return_msg_gettext(False,'Specified PHP version not installed') is_chattr = public.ExecShell('lsattr /usr|grep /usr/bin')[0].find('-i-') if is_chattr != -1: public.ExecShell('chattr -i /usr/bin') public.ExecShell("rm -f " + php_bin + ' '+ php_ize + ' ' + php_fpm + ' ' + php_pecl + ' ' + php_pear) @@ -1291,9 +1526,11 @@ class config: public.ExecShell("ln -sf %s %s" % (php_fpm_src,php_fpm)) public.ExecShell("ln -sf %s %s" % (php_pecl_src,php_pecl)) public.ExecShell("ln -sf %s %s" % (php_pear_src,php_pear)) + import jobs + jobs.set_php_cli_env() if is_chattr != -1: public.ExecShell('chattr +i /usr/bin') - public.WriteLog('P_CONF','SET_PHP_CLI %s' % get.php_version) - return public.returnMsg(True,'SET_SUCCESS') + public.write_log_gettext('Panel settings','Set the PHP-CLI version to: {}',(get.php_version,)) + return public.return_msg_gettext(True,'Setup successfully!') #获取BasicAuth状态 @@ -1317,6 +1554,8 @@ class config: tips = '_bt.cn' path = 'config/basic_auth.json' ba_conf = None + if is_open: + if not get.basic_user.strip() or not get.basic_pwd.strip(): return public.returnMsg(False,'BasicAuth authentication username and password cannot be empty!') if os.path.exists(path): try: ba_conf = json.loads(public.readFile(path)) @@ -1332,22 +1571,23 @@ class config: public.writeFile(path,json.dumps(ba_conf)) os.chmod(path,384) - public.WriteLog('P_CONF','SET_BASICAUTH_STATUS %s' % is_open) + public.write_log_gettext('Panel settings','Set the BasicAuth status to: {}', (is_open,)) public.writeFile('data/reload.pl','True') - return public.returnMsg(True,"SET_SUCCESS") + return public.return_msg_gettext(True,'Setup successfully!') #取面板运行日志 def get_panel_error_logs(self,get): filename = 'logs/error.log' - if not os.path.exists(filename): return public.returnMsg(False,'LOG_CLOSE') + if not os.path.exists(filename): return public.return_msg_gettext(False,"Logs emptied") result = public.GetNumLines(filename,2000) - return public.returnMsg(True,result) + return public.return_msg_gettext(True,result) + #清空面板运行日志 def clean_panel_error_logs(self,get): filename = 'logs/error.log' public.writeFile(filename,'') - public.WriteLog('P_CONF','CLEARING_LOG') - return public.returnMsg(True,'CLEARED') + public.write_log_gettext('Panel settings','Clearing log info') + return public.return_msg_gettext(True,'Cleared!') # 获取lets证书 def get_cert_source(self,get): @@ -1365,9 +1605,9 @@ class config: else: t_str = 'Open' public.writeFile(debug_path,'True') - public.WriteLog('TYPE_PANEL','DEVELOPER_MODE',(t_str,)) + public.write_log_gettext('Panel configuration','{} Developer mode(DeBug)',(t_str,)) public.restart_panel() - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #设置离线模式 @@ -1379,8 +1619,8 @@ class config: else: t_str = 'Open' public.writeFile(d_path,'True') - public.WriteLog('TYPE_PANEL','OFFLINE_MODE',(t_str,)) - return public.returnMsg(True,'SET_SUCCESS') + public.write_log_gettext('Panel configuration','{} Offline mode',(t_str,)) + return public.return_msg_gettext(True,'Setup successfully!') # 修改.user.ini文件 def _edit_user_ini(self,file,s_conf,act,session_path): @@ -1408,7 +1648,7 @@ class config: :return: ''' if public.get_webserver() == 'openlitespeed': - return public.returnMsg(False, "NOT_SUPPORT_OLS") + return public.return_msg_gettext(False, 'The current web server is openlitespeed. This function is not supported yet.') import panelSite site_info = public.M('sites').where('id=?', (get.id,)).field('name,path').find() session_path = "/www/php_session/{}".format(site_info["name"]) @@ -1422,12 +1662,12 @@ class config: if not os.path.exists(user_ini_file): public.writeFile(user_ini_file,conf) public.ExecShell("chattr +i {}".format(user_ini_file)) - return public.returnMsg(True,"SET_SUCCESS") + return public.return_msg_gettext(True,'Setup successfully!') self._edit_user_ini(user_ini_file,conf,get.act,session_path) - return public.returnMsg(True, "SET_SUCCESS") + return public.return_msg_gettext(True, 'Setup successfully!') else: self._edit_user_ini(user_ini_file,conf,get.act,session_path) - return public.returnMsg(True, "SET_SUCCESS") + return public.return_msg_gettext(True, 'Setup successfully!') # 获取php_session是否存放到独立文件夹 def get_php_session_path(self,get): @@ -1450,9 +1690,9 @@ class config: key = public.readFile(self._key_file) username = public.readFile(self._username_file) if not key: - return public.returnMsg(False, "KEY_NOT_EXIST") + return public.return_msg_gettext(False, 'The key does not exist. Please turn on and try again.') if not username: - return public.returnMsg(False, "USERNAME_NOT_EXIST1") + return public.return_msg_gettext(False, 'The username does not exist. Please turn on and try again.') return {"key":key,"username":username} def get_random(self): @@ -1466,7 +1706,7 @@ class config: def set_two_step_auth(self,get): if not hasattr(get,"act") or not get.act: - return public.returnMsg(False, "SELECT_MODE") + return public.return_msg_gettext(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) @@ -1481,7 +1721,7 @@ class config: username = public.readFile(self._username_file) local_ip = public.GetLocalIp() if not secret_key: - return public.returnMsg(False,"GENERATE_KEY_ERR",(self._setup_path+"/data/",)) + return public.return_msg_gettext(False,"Failed to generate key or username. Please check if the hard disk space is insufficient or the directory cannot be written.[ {} ]",(self._setup_path+"/data/",)) try: try: panel_name = json.loads(public.readFile(self._setup_path+'/config/config.json'))['title'] @@ -1489,36 +1729,36 @@ class config: panel_name = 'aaPanel' data = pyotp.totp.TOTP(secret_key).provisioning_uri(username, issuer_name='{}--{}'.format(panel_name,local_ip)) public.writeFile(self._core_fle_path+'/qrcode.txt',str(data)) - return public.returnMsg(True, "OPEN_SUCCESSFUL") + return public.return_msg_gettext(True, 'Setup successfully!') except Exception as e: - return public.returnMsg(False, e) + return public.return_msg_gettext(False, e) else: if os.path.exists(self._key_file): os.rename(self._key_file,self._bk_key_file) - return public.returnMsg(True, "CLOSE_SUCCESS") + return public.return_msg_gettext(True, 'Setup successfully!') # 检测是否开启双因素验证 def check_two_step(self,get): secret_key = public.readFile(self._key_file) if not secret_key: - return public.returnMsg(False, "GOOGLE_AUTH_ERR") - return public.returnMsg(True, "TURN_ON_GOOGLE_AUTH") + return public.return_msg_gettext(False, 'Did not open Google authentication') + return public.return_msg_gettext(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, "QR_CODE_ERR") + return public.return_msg_gettext(True, 'No QR code data, please re-open') # 设置是否云控打开 def set_coll_open(self,get): - if not 'coll_show' in get: return public.returnMsg(False,'INIT_ARGS_ERR') + if not 'coll_show' in get: return public.return_msg_gettext(False,'Parameter ERROR!') if get.coll_show == 'True': session['tmp_login'] = True else: session['tmp_login'] = False - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') # 获取菜单列表 def get_menu_list(self, get): @@ -1569,8 +1809,8 @@ class config: if h in not_hide_id: continue hide_menu.append(h) public.writeFile(hide_menu_file, json.dumps(hide_menu)) - public.WriteLog('TYPE_CONFIG', 'EDIT_MENU_SUCCESS') - return public.returnMsg(True, 'SET_SUCCESS') + public.write_log_gettext('Panel setting', 'Successfully modify the panel menu display list') + return public.return_msg_gettext(True, 'Setup successfully!') # 获取临时登录列表 def get_temp_login(self, args): @@ -1579,7 +1819,7 @@ class config: @author hwliang<2020-09-2> @return dict ''' - if 'tmp_login_expire' in session: return public.returnMsg(False, 'PERMISSION_DENIED') + if 'tmp_login_expire' in session: return public.return_msg_gettext(False, 'Permission denied!') public.M('temp_login').where('state=? and expire @return dict ''' - if 'tmp_login_expire' in session: return public.returnMsg(False, 'PERMISSION_DENIED') + if 'tmp_login_expire' in session: return public.return_msg_gettext(False, 'Permission denied!') s_time = int(time.time()) public.M('temp_login').where('state=? and expire>?', (0, s_time)).delete() token = public.GetRandomString(48) @@ -1627,9 +1867,9 @@ class config: pdata['id'] = 101 if public.M('temp_login').insert(pdata): - public.WriteLog('TYPE_CONFIG', 'TMP_LOGIN',(public.format_date(times=pdata['expire']),)) - return {'status': True, 'msg': public.getMsg('TMP_LOGIN1'), 'token': token, 'expire': pdata['expire']} - return public.returnMsg(False, 'TMP_LOGIN2') + public.write_log_gettext('Panel setting', 'Generate temporary connection, expiration time: {}',(public.format_date(times=pdata['expire']),)) + return {'status': True, 'msg': public.get_msg_gettext('Temporary login URL has been generated'), 'token': token, 'expire': pdata['expire']} + return public.return_msg_gettext(False, 'Failed to generate temporary login URL') # 删除临时登录 def remove_temp_login(self, args): @@ -1641,12 +1881,12 @@ class config: } @return dict ''' - if 'tmp_login_expire' in session: return public.returnMsg(False, 'PERMISSION_DENIED') + if 'tmp_login_expire' in session: return public.return_msg_gettext(False, 'Permission denied!') id = int(args.id) if public.M('temp_login').where('id=?', (id,)).delete(): - public.WriteLog('TYPE_CONFIG', 'TMP_LOGIN3') - return public.returnMsg(True, 'DEL_SUCCESS') - return public.returnMsg(False, 'DEL_ERROR') + public.write_log_gettext('Panel setting', 'Delete temporary login URL') + return public.return_msg_gettext(True, 'Successfully deleted') + return public.return_msg_gettext(False, 'Failed to delete') # 强制弹出指定临时登录 def clear_temp_login(self, args): @@ -1658,14 +1898,14 @@ class config: } @return dict ''' - if 'tmp_login_expire' in session: return public.returnMsg(False, 'PERMISSION_DENIED') + if 'tmp_login_expire' in session: return public.return_msg_gettext(False, 'Permission denied!') id = int(args.id) s_file = 'data/session/{}'.format(id) if os.path.exists(s_file): os.remove(s_file) - public.WriteLog('TYPE_CONFIG', 'LOGOUT_TMP_UESR',(str(id),)) - return public.returnMsg(True, 'LOGOUT_TMP_USER',(str(id),)) - public.returnMsg(False, 'TMP_USER_NOT_LOGIN') + public.write_log_gettext('Panel setting', 'Force logout of temporary users:{1}',(str(id),)) + return public.return_msg_gettext(True, 'Temporary user has been forcibly logged out:{}',(str(id),)) + public.return_msg_gettext(False, 'The specified user is not currently logged in!') # 查看临时授权操作日志 def get_temp_login_logs(self, args): @@ -1677,7 +1917,7 @@ class config: } @return dict ''' - if 'tmp_login_expire' in session: return public.returnMsg(False, 'PERMISSION_DENIED') + if 'tmp_login_expire' in session: return public.return_msg_gettext(False, 'Permission denied!') id = int(args.id) data = public.M('logs').where('uid=?', (id,)).order('id desc').select() return data @@ -1750,8 +1990,8 @@ class config: else: result['dingding']=False if result['mail'] or result['dingding']: - return public.returnMsg(True, result) - return public.returnMsg(False, result) + return public.return_msg_gettext(True, result) + return public.return_msg_gettext(False, result) #设置告警 def set_login_send(self,get): @@ -1761,15 +2001,15 @@ class config: os.mknod("/www/server/panel/data/login_send_mail.pl") if os.path.exists("/www/server/panel/data/login_send_dingding.pl"): os.remove("/www/server/panel/data/login_send_dingding.pl") - return public.returnMsg(True, 'Setup Successfully') + return public.return_msg_gettext(True, 'Setup Successfully') elif type=='dingding': if not os.path.exists("/www/server/panel/data/login_send_dingding.pl"): os.mknod("/www/server/panel/data/login_send_dingding.pl") if os.path.exists("/www/server/panel/data/login_send_mail.pl"): os.remove("/www/server/panel/data/login_send_mail.pl") - return public.returnMsg(True, 'Setup Successfully') + return public.return_msg_gettext(True, 'Setup Successfully') else: - return public.returnMsg(False,'The delivery type is not supported') + return public.return_msg_gettext(False,'The delivery type is not supported') #取消告警 def clear_login_send(self,get): @@ -1777,13 +2017,13 @@ class config: if type == 'mail': if os.path.exists("/www/server/panel/data/login_send_mail.pl"): os.remove("/www/server/panel/data/login_send_mail.pl") - return public.returnMsg(True, '取消成功') + return public.return_msg_gettext(True, 'Setup successfully!') elif type == 'dingding': if os.path.exists("/www/server/panel/data/login_send_dingding.pl"): os.remove("/www/server/panel/data/login_send_dingding.pl") - return public.returnMsg(True, '取消成功') + return public.return_msg_gettext(True, 'Setup successfully!') else: - return public.returnMsg(False, '不支持该发送类型') + return public.return_msg_gettext(False, 'The send type is not supported') #告警日志 def get_login_log(self,get): @@ -1826,11 +2066,11 @@ class config: try: path='/www/server/panel/data/send_login_white.json' ip_white=json.loads(public.ReadFile('/www/server/panel/data/send_login_white.json')) - if not ip_white:return public.returnMsg(True, []) - return public.returnMsg(True, ip_white) + if not ip_white:return public.return_msg_gettext(True, []) + return public.return_msg_gettext(True, ip_white) except: public.WriteFile(path, '[]') - return public.returnMsg(True, []) + return public.return_msg_gettext(True, []) def add_login_ipwhite(self,get): ip=get.ip.strip() @@ -1840,10 +2080,10 @@ class config: if not ip in ip_white: ip_white.append(ip) public.WriteFile(path, json.dumps(ip_white)) - return public.returnMsg(True, "Add successfully") + return public.return_msg_gettext(True, "Setup successfully!") except: public.WriteFile(path, json.dumps([ip])) - return public.returnMsg(True, "Add successfully") + return public.return_msg_gettext(True, "Setup successfully!") def del_login_ipwhite(self,get): ip = get.ip.strip() @@ -1853,34 +2093,68 @@ class config: if ip in ip_white: ip_white.remove(ip) public.WriteFile(path, json.dumps(ip_white)) - return public.returnMsg(True, "Delete successfully") + return public.return_msg_gettext(True, "Successfully deleted!") except: public.WriteFile(path, json.dumps([])) - return public.returnMsg(True, "Delete successfully") + return public.return_msg_gettext(True, "Successfully deleted!") def clear_login_ipwhite(self,get): path = '/www/server/panel/data/send_login_white.json' public.WriteFile(path, json.dumps([])) - return public.returnMsg(True, "Clear successfully") + return public.return_msg_gettext(True, "Successfully created") def get_panel_ssl_status(self,get): import os if os.path.exists(self._setup_path+'/data/ssl.pl'): - return public.returnMsg(True,'success') - return public.returnMsg(False,'false') + return public.return_msg_gettext(True,'success') + return public.return_msg_gettext(False,'false') def set_backup_notification(self,get): import os f = self._setup_path+'/data/send_back_error.pl' if os.path.exists(f): public.ExecShell('rm -f {}'.format(f)) - return public.returnMsg(True,'Disable successfully') + return public.return_msg_gettext(True,'Disable successfully') public.writeFile(f,'1') - return public.returnMsg(True,'Enable Successfully') + return public.return_msg_gettext(True,'Enable Successfully') def get_backup_notification(self,get): import os if os.path.exists(self._setup_path+'/data/send_back_error.pl'): return public.returnMsg(True,'success') - return public.returnMsg(False,'false') \ No newline at end of file + return public.returnMsg(False,'false') + + def set_not_auth_status(self,get): + ''' + @name 设置未认证时的响应状态 + @author hwliang<2021-12-16> + @param status_code 状态码 + @return dict + ''' + if not 'status_code' in get: + return public.return_msg_gettext(False,'Parameter ERROR!') + + if re.match("^\d+$", get.status_code): + status_code = int(get.status_code) + if status_code != 0: + if status_code < 100 or status_code > 999: + return public.return_msg_gettext(False,'Parameter ERROR!') + else: + return public.return_msg_gettext(False,'Parameter ERROR!') + + public.save_config('abort',get.status_code) + public.write_log_gettext('Panel configuration','Set the unauthorized response status to:{}'.format(get.status_code)) + return public.return_msg_gettext(True,'Setup successfully!') + + def get_not_auth_status(self): + ''' + @name 获取未认证时的响应状态 + @author hwliang<2021-12-16> + @return int + ''' + try: + status_code = int(public.read_config('abort')) + return status_code + except: + return 404 \ No newline at end of file diff --git a/class/crontab.py b/class/crontab.py index 07a64b9e..7f4fdebd 100644 --- a/class/crontab.py +++ b/class/crontab.py @@ -33,27 +33,27 @@ class crontab: tmp = {} tmp=cront[i] if cront[i]['type']=="day": - tmp['type']=public.getMsg('CRONTAB_TODAY') - tmp['cycle']= public.getMsg('CRONTAB_TODAY_CYCLE',(str(cront[i]['where_hour']),str(cront[i]['where_minute']))) + tmp['type']=public.get_msg_gettext('Per Day') + tmp['cycle']= public.get_msg_gettext('Per Day, run at {} Hour {} Min',(str(cront[i]['where_hour']),str(cront[i]['where_minute']))) elif cront[i]['type']=="day-n": - tmp['type']=public.getMsg('CRONTAB_N_TODAY',(str(cront[i]['where1']),)) - tmp['cycle']=public.getMsg('CRONTAB_N_TODAY_CYCLE',(str(cront[i]['where1']),str(cront[i]['where_hour']),str(cront[i]['where_minute']))) + tmp['type']=public.get_msg_gettext('Every {} Days',(str(cront[i]['where1']),)) + tmp['cycle']=public.get_msg_gettext('Every {} Days, run at {} Hour {} Min',(str(cront[i]['where1']),str(cront[i]['where_hour']),str(cront[i]['where_minute']))) elif cront[i]['type']=="hour": - tmp['type']=public.getMsg('CRONTAB_HOUR') - tmp['cycle']=public.getMsg('CRONTAB_HOUR_CYCLE',(str(cront[i]['where_minute']),)) + tmp['type']=public.get_msg_gettext('Per Hour') + tmp['cycle']=public.get_msg_gettext('Per Hour, run at {} Min',(str(cront[i]['where_minute']),)) elif cront[i]['type']=="hour-n": - tmp['type']=public.getMsg('CRONTAB_N_HOUR',(str(cront[i]['where1']),)) - tmp['cycle']=public.getMsg('CRONTAB_N_HOUR_CYCLE',(str(cront[i]['where1']),str(cront[i]['where_minute']))) + tmp['type']=public.get_msg_gettext('Every {} Hours',(str(cront[i]['where1']),)) + tmp['cycle']=public.get_msg_gettext('Every {} Hours, run at {} Min',(str(cront[i]['where1']),str(cront[i]['where_minute']))) elif cront[i]['type']=="minute-n": - tmp['type']=public.getMsg('CRONTAB_N_MINUTE',(str(cront[i]['where1']),)) - tmp['cycle']=public.getMsg('CRONTAB_N_MINUTE_CYCLE',(str(cront[i]['where1']),)) + tmp['type']=public.get_msg_gettext('Every {} Minutes',(str(cront[i]['where1']),)) + tmp['cycle']=public.get_msg_gettext('Run Every {} Minutes',(str(cront[i]['where1']),)) elif cront[i]['type']=="week": - tmp['type']=public.getMsg('CRONTAB_WEEK') + tmp['type']=public.get_msg_gettext('Weekly') if not cront[i]['where1']: cront[i]['where1'] = '0' - tmp['cycle']= public.getMsg('CRONTAB_WEEK_CYCLE',(self.toWeek(int(cront[i]['where1'])),str(cront[i]['where_hour']),str(cront[i]['where_minute']))) + tmp['cycle']= public.get_msg_gettext('Every {}, run at {} Hour {} Min',(self.toWeek(int(cront[i]['where1'])),str(cront[i]['where_hour']),str(cront[i]['where_minute']))) elif cront[i]['type']=="month": - tmp['type']=public.getMsg('CRONTAB_MONTH') - tmp['cycle']=public.getMsg('CRONTAB_MONTH_CYCLE',(str(cront[i]['where1']),str(cront[i]['where_hour']),str(cront[i]['where_minute']))) + tmp['type']=public.get_msg_gettext('Monthly') + tmp['cycle']=public.get_msg_gettext('Monthly, run on {}Day {} Hour {}Min',(str(cront[i]['where1']),str(cront[i]['where_hour']),str(cront[i]['where_minute']))) log_file = '/www/server/cron/{}.log'.format(tmp['echo']) if os.path.exists(log_file): @@ -80,13 +80,13 @@ class crontab: #转换大写星期 def toWeek(self,num): wheres={ - 0 : public.getMsg('CRONTAB_SUNDAY'), - 1 : public.getMsg('CRONTAB_MONDAY'), - 2 : public.getMsg('CRONTAB_TUESDAY'), - 3 : public.getMsg('CRONTAB_WEDNESDAY'), - 4 : public.getMsg('CRONTAB_THURSDAY'), - 5 : public.getMsg('CRONTAB_FRIDAY'), - 6 : public.getMsg('CRONTAB_SATURDAY') + 0 : public.get_msg_gettext('Sunday'), + 1 : public.get_msg_gettext('Monday'), + 2 : public.get_msg_gettext('Tuesday'), + 3 : public.get_msg_gettext('Wednesday'), + 4 : public.get_msg_gettext('Thursday'), + 5 : public.get_msg_gettext('Friday'), + 6 : public.get_msg_gettext('Saturday') } try: return wheres[num] @@ -132,12 +132,12 @@ class crontab: public.M('crontab').where('id=?',(id,)).setField('status',status) public.WriteLog('TYPE_CRON',"MODIFY_CRON_STATUS",(cronInfo['name'],str(status_msg[status]))) - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #修改计划任务 def modify_crond(self,get): if len(get['name'])<1: - return public.returnMsg(False,'CRONTAB_TASKNAME_EMPTY') + return public.return_msg_gettext(False,'Name of task cannot be empty!') id = get['id'] cuonConfig,get,name = self.GetCrondCycle(get) cronInfo = public.M('crontab').where('id=?',(id,)).field(self.field).find() @@ -167,7 +167,7 @@ class crontab: self.remove_for_crond(cronInfo['echo']) self.sync_to_crond(cronInfo) public.WriteLog('TYPE_CRON',"MODIFY_CRON",(cronInfo['name'])) - return public.returnMsg(True,'EDIT_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #获取指定任务数据 @@ -196,7 +196,7 @@ class crontab: #添加计划任务 def AddCrontab(self,get): if len(get['name'])<1: - return public.returnMsg(False,'CRONTAB_TASKNAME_EMPTY') + return public.return_msg_gettext(False,'Name of task cannot be empty!') cuonConfig,get,name = self.GetCrondCycle(get) cronPath=public.GetConfigValue('setup_path')+'/cron' cronName=self.GetShell(get) @@ -208,22 +208,22 @@ class crontab: self.CrondReload() columns = 'name,type,where1,where_hour,where_minute,echo,addtime,\ status,save,backupTo,sType,sName,sBody,urladdress' - values = (public.xssencode(get['name']),get['type'],get['where1'],get['hour'], + values = (public.xssencode2(get['name']),get['type'],get['where1'],get['hour'], get['minute'],cronName,time.strftime('%Y-%m-%d %X',time.localtime()), 1,get['save'],get['backupTo'],get['sType'],get['sName'],get['sBody'], get['urladdress']) if "save_local" in get: columns += ",save_local,notice,notice_channel" - values = (public.xssencode(get['name']),get['type'],get['where1'],get['hour'], + values = (public.xssencode2(get['name']),get['type'],get['where1'],get['hour'], get['minute'],cronName,time.strftime('%Y-%m-%d %X',time.localtime()), 1,get['save'],get['backupTo'],get['sType'],get['sName'],get['sBody'], get['urladdress'], get["save_local"], get['notice'], get['notice_channel']) addData=public.M('crontab').add(columns,values) if addData>0: - result = public.returnMsg(True,'ADD_SUCCESS') + result = public.return_msg_gettext(True,'Setup successfully!') result['id'] = addData return result - return public.returnMsg(False,'ADD_ERROR') + return public.return_msg_gettext(False,'Failed to add') #构造周期 def GetCrondCycle(self,params): @@ -231,16 +231,16 @@ class crontab: name = "" if params['type']=="day": cuonConfig = self.GetDay(params) - name = public.getMsg('CRONTAB_TODAY') + name = public.get_msg_gettext('Per Day') elif params['type']=="day-n": cuonConfig = self.GetDay_N(params) - name = public.getMsg('CRONTAB_N_TODAY',(params['where1'],)) + name = public.get_msg_gettext('Every {0} Days',(params['where1'],)) elif params['type']=="hour": cuonConfig = self.GetHour(params) - name = public.getMsg('CRONTAB_HOUR') + name = public.get_msg_gettext('Per Hour') elif params['type']=="hour-n": cuonConfig = self.GetHour_N(params) - name = public.getMsg('CRONTAB_HOUR') + name = public.get_msg_gettext('Per Hour') elif params['type']=="minute-n": cuonConfig = self.Minute_N(params) elif params['type']=="week": @@ -252,36 +252,36 @@ class crontab: #取任务构造Day def GetDay(self,param): - cuonConfig ="{0} {1} * * * ".format(param['minute'],param['hour']) + cuonConfig ="{} {} * * * ".format(param['minute'],param['hour']) return cuonConfig #取任务构造Day_n def GetDay_N(self,param): - cuonConfig ="{0} {1} */{2} * * ".format(param['minute'],param['hour'],param['where1']) + cuonConfig ="{} {} */{} * * ".format(param['minute'],param['hour'],param['where1']) return cuonConfig #取任务构造Hour def GetHour(self,param): - cuonConfig ="{0} * * * * ".format(param['minute']) + cuonConfig ="{} * * * * ".format(param['minute']) return cuonConfig #取任务构造Hour-N def GetHour_N(self,param): - cuonConfig ="{0} */{1} * * * ".format(param['minute'],param['where1']) + cuonConfig ="{} */{} * * * ".format(param['minute'],param['where1']) return cuonConfig #取任务构造Minute-N def Minute_N(self,param): - cuonConfig ="*/{0} * * * * ".format(param['where1']) + cuonConfig ="*/{} * * * * ".format(param['where1']) return cuonConfig #取任务构造week def Week(self,param): - cuonConfig ="{0} {1} * * {2}".format(param['minute'],param['hour'],param['week']) + cuonConfig ="{} {} * * {}".format(param['minute'],param['hour'],param['week']) return cuonConfig #取任务构造Month def Month(self,param): - cuonConfig = "{0} {1} {2} * * ".format(param['minute'],param['hour'],param['where1']) + cuonConfig = "{} {} {} * * ".format(param['minute'],param['hour'],param['where1']) return cuonConfig #取数据列表 @@ -308,9 +308,9 @@ class crontab: id = get['id'] echo = public.M('crontab').where("id=?",(id,)).field('echo').find() logFile = public.GetConfigValue('setup_path')+'/cron/'+echo['echo']+'.log' - if not os.path.exists(logFile):return public.returnMsg(False, 'CRONTAB_TASKLOG_EMPTY') + if not os.path.exists(logFile):return public.return_msg_gettext(False, 'log is empty') log = public.GetNumLines(logFile,2000) - return public.returnMsg(True, log) + return public.return_msg_gettext(True, log) #清理任务日志 def DelLogs(self,get): @@ -319,16 +319,16 @@ class crontab: echo = public.M('crontab').where("id=?",(id,)).getField('echo') logFile = public.GetConfigValue('setup_path')+'/cron/'+echo+'.log' os.remove(logFile) - return public.returnMsg(True, 'CRONTAB_TASKLOG_CLOSE') + return public.return_msg_gettext(True, 'Logs emptied') except: - return public.returnMsg(False, 'CRONTAB_TASKLOG_CLOSE_ERR') + return public.return_msg_gettext(False, 'Failed to empty task logs!') #删除计划任务 def DelCrontab(self,get): try: id = get['id'] find = public.M('crontab').where("id=?",(id,)).field('name,echo').find() - if not self.remove_for_crond(find['echo']): return public.returnMsg(False,'SYSSAFE_CANT_WRITE_FILE') + if not self.remove_for_crond(find['echo']): return public.return_msg_gettext(False,'Unable to write to file, please check if system hardening is enabled!') cronPath = public.GetConfigValue('setup_path') + '/cron' sfile = cronPath + '/' + find['echo'] if os.path.exists(sfile): os.remove(sfile) @@ -337,9 +337,9 @@ class crontab: public.M('crontab').where("id=?",(id,)).delete() public.WriteLog('TYPE_CRON', 'CRONTAB_DEL',(find['name'],)) - return public.returnMsg(True, 'DEL_SUCCESS') + return public.return_msg_gettext(True, 'Successfully deleted') except: - return public.returnMsg(False, 'DEL_ERROR') + return public.return_msg_gettext(False, 'Failed to delete') #从crond删除 def remove_for_crond(self,echo): @@ -419,7 +419,7 @@ echo "-------------------------------------------------------------------------- public.ExecShell('chmod 750 ' + file) return cronName #except Exception as ex: - #return public.returnMsg(False, 'FILE_WRITE_ERR' + str(ex)) + #return public.return_msg_gettext(False, 'Failed to write in file!' + str(ex)) #检查脚本 def CheckScript(self,shell): @@ -443,7 +443,7 @@ echo "-------------------------------------------------------------------------- file = self.get_cron_file() 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!') + if type(conf)==bool:return public.return_msg_gettext(False,'Failed to read file!') conf += config + "\n" if public.writeFile(file,conf): if not os.path.exists(u_file): @@ -451,7 +451,7 @@ echo "-------------------------------------------------------------------------- else: public.ExecShell("chmod 600 '" + file + "' && chown root.crontab " + file) return True - return public.returnMsg(False,'SYSSAFE_CANT_WRITE_FILE') + return public.return_msg_gettext(False,'Unable to write to file, please check if system hardening is enabled!') #立即执行任务 def StartTask(self,get): @@ -459,7 +459,7 @@ echo "-------------------------------------------------------------------------- execstr = public.GetConfigValue('setup_path') + '/cron/' + echo public.ExecShell('chmod +x ' + execstr) public.ExecShell('nohup ' + execstr + ' >> ' + execstr + '.log 2>&1 &') - return public.returnMsg(True,'CRONTAB_TASK_EXEC') + return public.return_msg_gettext(True,'Task has been executed!') #获取计划任务文件位置 def get_cron_file(self): diff --git a/class/data.py b/class/data.py index ec8c74bf..b9b6b355 100644 --- a/class/data.py +++ b/class/data.py @@ -25,10 +25,10 @@ class data: ''' def setPs(self,get): id = get.id - get.ps = public.xssencode(get.ps) + get.ps = public.xssencode2(get.ps) if public.M(get.table).where("id=?",(id,)).setField('ps',get.ps): - return public.returnMsg(True,'EDIT_SUCCESS') - return public.returnMsg(False,'EDIT_ERROR') + return public.return_msg_gettext(True,'Setup successfully!') + return public.return_msg_gettext(False,'Failed to modify') #端口扫描 def CheckPort(self,port): @@ -142,7 +142,7 @@ class data: conf = public.readFile( self.setupPath + '/panel/vhost/' + self.web_server + '/detail/' + siteName + '.conf') if self.web_server == 'nginx': - rep = r"enable-php-(\w{2,5})\.conf" + rep = r"enable-php-(\w{2,5})[-\w]*\.conf" elif self.web_server == 'apache': rep = r"php-cgi-(\w{2,5})\.sock" else: @@ -172,6 +172,37 @@ class data: except: return 0 + def get_site_quota(self,path): + ''' + @name 获取网站目录配额信息 + @author hwliang<2022-02-15> + @param path 网站目录 + @return dict + ''' + res = {'size':0 ,'used':0 } + try: + from projectModel.quotaModel import main + quota_info = main().get_quota_path_list(get_path = path) + if isinstance(quota_info,dict): + return quota_info + return res + except: return res + + def get_database_quota(self,db_name): + ''' + @name 获取网站目录配额信息 + @author hwliang<2022-02-15> + @param path 网站目录 + @return dict + ''' + res = {'size':0 ,'used':0 } + try: + from projectModel.quotaModel import main + quota_info = main().get_quota_mysql_list(get_name = db_name) + if isinstance(quota_info,dict): + return quota_info + return res + except: return res ''' * 取数据列表 @@ -181,6 +212,7 @@ class data: * @return Json page.分页数 , count.总行数 data.取回的数据 ''' def getData(self,get): + import one_key_wp try: table = get.table data = self.GetSql(get) @@ -188,29 +220,55 @@ class data: if table == 'backup': import os + backup_path = public.M('config').where('id=?',(1,)).getField('backup_path') for i in range(len(data['data'])): if data['data'][i]['size'] == 0: - if os.path.exists(data['data'][i]['filename']): data['data'][i]['size'] = os.path.getsize(data['data'][i]['filename']) + if os.path.exists(data['data'][i]['filename']): + data['data'][i]['size'] = os.path.getsize(data['data'][i]['filename']) + else: + if not os.path.exists(data['data'][i]['filename']): + if (data['data'][i]['filename'].find('/www/') != -1 or data['data'][i]['filename'].find(backup_path) != -1) and data['data'][i]['filename'][0] == '/' and data['data'][i]['filename'].find('|') == -1: + data['data'][i]['size'] = 0 + data['data'][i]['ps'] = public.get_msg_gettext("File does not exist!") elif table == 'sites' or table == 'databases': type = '0' if table == 'databases': type = '1' for i in range(len(data['data'])): data['data'][i]['backup_count'] = SQL.table('backup').where("pid=? AND type=?",(data['data'][i]['id'],type)).count() + if table == 'databases': data['data'][i]['conn_config'] = json.loads(data['data'][i]['conn_config']) + data['data'][i]['quota'] = self.get_database_quota(data['data'][i]['name']) if table == 'sites': for i in range(len(data['data'])): data['data'][i]['domain'] = SQL.table('domain').where("pid=?",(data['data'][i]['id'],)).count() data['data'][i]['ssl'] = self.get_site_ssl_info(data['data'][i]['name']) data['data'][i]['php_version'] = self.get_php_version(data['data'][i]['name']) + data['data'][i]['attack'] = self.get_analysis(get,data['data'][i]) + data['data'][i]['project_type'] = SQL.table('sites').where('id=?',(data['data'][i]['id'])).field('project_type').find()['project_type'] + if data['data'][i]['project_type'] == 'WP': + data['data'][i]['cache_status'] = one_key_wp.one_key_wp().get_cache_status(data['data'][i]['id']) if not data['data'][i]['status'] in ['0','1',0,1]: data['data'][i]['status'] = '1' + data['data'][i]['quota'] = self.get_site_quota(data['data'][i]['path']) elif table == 'firewall': 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'] = self.CheckPort(int(data['data'][i]['port'])) - + + elif table == 'ftps': + for i in range(len(data['data'])): + data['data'][i]['quota'] = self.get_site_quota(data['data'][i]['path']) + + try: + for _find in data['data']: + _keys = _find.keys() + for _key in _keys: + _find[_key] = public.xsssec(_find[_key]) + except: + pass + #返回 return data except: @@ -229,15 +287,21 @@ class data: SQL = public.M(tableName) where = "id=?" find = SQL.where(where,(id,)).field(field).find() + try: + _keys = find.keys() + for _key in _keys: + find[_key] = public.xsssec(find[_key]) + except: + pass return find - - + + ''' * 取字段值 * @param String _GET['tab'] 数据库表名 * @param String _GET['key'] 字段 * @param String _GET['id'] 条件ID - * @return String + * @return String ''' def getKey(self,get): tableName = get.table @@ -246,7 +310,7 @@ class data: SQL = db.Sql().table(tableName) where = "id=?" retuls = SQL.where(where,(id,)).getField(keyName) - return retuls + return public.xsssec(retuls) ''' * 获取数据与分页 @@ -259,117 +323,138 @@ class data: def GetSql(self,get,result = '1,2,3,4,5,8'): #判断前端是否传入参数 order = "id desc" - if hasattr(get,'order'): - order = get.order - + if hasattr(get,'order'): + # 验证参数格式 + if re.match(r"^[\w\s\-\.]+$",get.order): + order = get.order + limit = 20 - if hasattr(get,'limit'): + if hasattr(get,'limit'): limit = int(get.limit) - - if hasattr(get,'result'): - result = get.result - + if limit < 1: limit = 20 + + if hasattr(get,'result'): + # 验证参数格式 + if re.match(r"^[\d\,]+$",get.result): + result = get.result + SQL = db.Sql() data = {} #取查询条件 where = '' + param = () if hasattr(get,'search'): if sys.version_info[0] == 2: get.search = get.search.encode('utf-8') - where = self.GetWhere(get.table,get.search) + where,param = self.GetWhere(get.table,get.search) if get.table == 'backup': - where += " and type='" + get.type+"'" - + where += " and type='{}'".format(int(get.type)) + if get.table == 'sites' and get.search: - pid = SQL.table('domain').where("name LIKE '%"+get.search+"%'",()).getField('pid') + pid = SQL.table('domain').where("name LIKE ?",("%{}%".format(get.search),)).getField('pid') if pid: if where: where += " or id=" + str(pid) else: where += "id=" + str(pid) - if get.table == 'sites' and hasattr(get,'type'): - if get.type != '-1': - type_where = "type_id=%s" % get.type - if where == '': - where = type_where - else: - where += " and " + type_where if get.table == 'sites': if where: where = "({}) AND project_type='PHP'".format(where) else: - where = "project_type='PHP'" + where = "(project_type='PHP' OR project_type='WP')" + if hasattr(get,'type'): + if get.type != '-1': + where += " AND type_id={}".format(int(get.type)) + if get.table == 'databases': + if hasattr(get,'db_type'): + if where: + where += " AND db_type='{}'".format(int(get.db_type)) + else: + where = "db_type='{}'".format(int(get.db_type)) + if hasattr(get,'sid'): + if where: + where += " AND sid='{}'".format(int(get.sid)) + else: + where = "sid='{}'".format(int(get.sid)) field = self.GetField(get.table) #实例化数据库对象 - - + + #是否直接返回所有列表 if hasattr(get,'list'): - data = SQL.table(get.table).where(where,()).field(field).order(order).select() + data = SQL.table(get.table).where(where,param).field(field).order(order).select() return data - + #取总行数 - count = SQL.table(get.table).where(where,()).count() + count = SQL.table(get.table).where(where,param).count() #get.uri = get #包含分页类 import page #实例化分页类 page = page.Page() - + info = {} info['count'] = count info['row'] = limit - + info['p'] = 1 if hasattr(get,'p'): info['p'] = int(get['p']) - info['uri'] = get + if info['p'] <1: info['p'] = 1 + + try: + from flask import request + info['uri'] = public.url_encode(request.full_path) + except: + info['uri'] = '' info['return_js'] = '' if hasattr(get,'tojs'): - info['return_js'] = get.tojs - + if re.match(r"^[\w\.\-]+$",get.tojs): + info['return_js'] = get.tojs + data['where'] = where - + #获取分页数据 data['page'] = page.GetPage(info,result) #取出数据 - data['data'] = SQL.table(get.table).where(where,()).order(order).field(field).limit(str(page.SHIFT)+','+str(page.ROW)).select() + data['data'] = SQL.table(get.table).where(where,param).order(order).field(field).limit(str(page.SHIFT)+','+str(page.ROW)).select() return data - + #获取条件 - def GetWhere(self,tableName,search): - if not search: return "" + def GetWhere(self,tableName,search): + if not search: return "",() if type(search) == bytes: search = search.encode('utf-8').strip() try: - search = re.search(r"[\w\x80-\xff\.]+",search).group() + search = re.search(r"[\w\x80-\xff\.\_\-]+",search).group() except: - return '' + return '',() wheres = { - 'sites' : "id='"+search+"' or name like '%"+search+"%' or status like '%"+search+"%' or ps like '%"+search+"%'", - 'ftps' : "id='"+search+"' or name like '%"+search+"%' or ps like '%"+search+"%'", - 'databases' : "id='"+search+"' or name like '%"+search+"%' or ps like '%"+search+"%'", - 'logs' : "uid='"+search+"' or username='"+search+"' or type like '%"+search+"%' or log like '%"+search+"%' or addtime like '%"+search+"%'", - 'backup' : "pid="+search+"", - 'users' : "id='"+search+"' or username='"+search+"'", - 'domain' : "pid='"+search+"' or name='"+search+"'", - 'tasks' : "status='"+search+"' or type='"+search+"'" + 'sites' : ("name LIKE ? OR ps LIKE ?",('%'+search+'%','%'+search+'%')), + 'ftps' : ("name LIKE ? OR ps LIKE ?",('%'+search+'%','%'+search+'%')), + 'databases' : ("(name LIKE ? OR ps LIKE ?)",("%"+search+"%","%"+search+"%")), + 'logs' : ("username=? OR type LIKE ? OR log LIKE ?",(search,'%'+search+'%','%'+search+'%')), + 'backup' : ("pid=?",(search,)), + 'users' : ("id='?' OR username=?",(search,search)), + 'domain' : ("pid=? OR name=?",(search,search)), + 'tasks' : ("status=? OR type=?",(search,search)), } try: return wheres[tableName] except: - return '' - + return '',() + + # 获取返回的字段 def GetField(self,tableName): fields = { 'sites' : "id,name,path,status,ps,addtime,edate", 'ftps' : "id,pid,name,password,status,ps,addtime,path", - 'databases' : "id,pid,name,username,password,accept,ps,addtime", + 'databases' : "id,sid,pid,name,username,password,accept,ps,addtime,db_type,conn_config", 'logs' : "id,uid,username,type,log,addtime", - 'backup' : "id,pid,name,filename,addtime,size", + 'backup' : "id,pid,name,filename,addtime,size,ps", 'users' : "id,username,phone,email,login_ip,login_time", 'firewall' : "id,port,ps,addtime", 'domain' : "id,pid,name,port,addtime", @@ -379,3 +464,10 @@ class data: return fields[tableName] except: return '' + + def get_analysis(self,get,i): + import log_analysis + get.path = '/www/wwwlogs/{}.log'.format(i['name']) + get.action = 'get_result' + data = log_analysis.log_analysis().get_result(get) + return int(data['php']) + int(data['san']) + int(data['sql']) + int(data['xss']) \ No newline at end of file diff --git a/class/database.py b/class/database.py index fcce1c3a..d694ccb9 100644 --- a/class/database.py +++ b/class/database.py @@ -15,6 +15,246 @@ from BTPanel import session import datatool class database(datatool.datatools): sqlite_connection = None + sid = 0 + + def AddCloudServer(self, get): + ''' + @name 添加远程服务器 + @author hwliang<2021-01-10> + @param db_host 服务器地址 + @param db_port 数据库端口 + @param db_user 用户名 + @param db_password 数据库密码 + @param db_ps 数据库备注 + @return dict + ''' + if not hasattr(get, 'db_host'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + if not hasattr(get, 'db_port'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + if not hasattr(get, 'db_user'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + if not hasattr(get, 'db_password'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + if not hasattr(get, 'db_ps'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + get.db_name = None + res = self.CheckCloudDatabase(get) + if isinstance(res, dict): return res + if public.M('database_servers').where('db_host=? AND db_port=?', (get.db_host, get.db_port)).count(): + return public.return_msg_gettext(False, 'The specified server already exists: [{}:{}]',(get.db_host, get.db_port)) + get.db_port = int(get.db_port) + pdata = { + 'db_host': get.db_host, + 'db_port': get.db_port, + 'db_user': get.db_user, + 'db_password': get.db_password, + 'ps': public.xssencode2(get.db_ps.strip()), + 'addtime': int(time.time()) + } + + result = public.M("database_servers").insert(pdata) + + if isinstance(result, int): + public.write_log_gettext('Database manager', 'Add remote MySQL server [{}:{}]',(get.db_host, get.db_port)) + return public.return_msg_gettext(True, 'Setup successfully!') + return public.return_msg_gettext(False, 'Add failed: {}',(result,)) + + def GetCloudServer(self, get): + ''' + @name 获取远程服务器列表 + @author hwliang<2021-01-10> + @return list + ''' + data = public.M('database_servers').where("db_type=?",("mysql",)).select() + bt_mysql_bin = '{}/mysql/bin/mysql'.format(public.get_setup_path()) + + if not isinstance(data,list): data = [] + if os.path.exists(bt_mysql_bin): + data.insert(0,{'id':0,'db_host':'127.0.0.1','db_port':3306,'db_user':'root','db_password':'','ps':'LocalServer','addtime':0}) + return data + + def RemoveCloudServer(self, get): + ''' + @name 删除远程服务器 + @author hwliang<2021-01-10> + @param id 远程服务器ID + @return dict + ''' + + id = int(get.id) + if not id: return public.return_msg_gettext(False, 'Parameter ERROR!') + db_find = public.M('database_servers').where('id=?', (id,)).find() + if not db_find: return public.returnMsg(False, 'The specified server dose not exists!') + public.M('databases').where('sid=?', id).delete() + result = public.M('database_servers').where('id=?', id).delete() + if isinstance(result, int): + public.write_log_gettext('Database manager', 'Delete the remote MySQL server [{}:{}]',(db_find['db_host'], int(db_find['db_port']))) + return public.return_msg_gettext(True, 'Successfully deleted!') + return public.return_msg_gettext(False, 'Failed to delete: {}',(result,)) + + def ModifyCloudServer(self, get): + ''' + @name 修改远程服务器 + @author hwliang<2021-01-10> + @param id 远程服务器ID + @param db_host 服务器地址 + @param db_port 数据库端口 + @param db_user 用户名 + @param db_password 数据库密码 + @param db_ps 数据库备注 + @return dict + ''' + if not hasattr(get, 'id'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + if not hasattr(get, 'db_host'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + if not hasattr(get, 'db_port'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + if not hasattr(get, 'db_user'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + if not hasattr(get, 'db_password'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + if not hasattr(get, 'db_ps'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + + id = int(get.id) + get.db_port = int(get.db_port) + db_find = public.M('database_servers').where('id=?', (id,)).find() + if not db_find: return public.return_msg_gettext(False, 'The specified server dose not exists!') + _modify = False + if db_find['db_host'] != get.db_host or db_find['db_port'] != get.db_port: + _modify = True + if public.M('database_servers').where('db_host=? AND db_port=?', (get.db_host, get.db_port)).count(): + return public.return_msg_gettext(False, 'The specified server already exists: [{}:{}]',(get.db_host, get.db_port)) + + if db_find['db_user'] != get.db_user or db_find['db_password'] != get.db_password: + _modify = True + + if _modify: + res = self.CheckCloudDatabase(get) + if isinstance(res, dict): return res + + pdata = { + 'db_host': get.db_host, + 'db_port': get.db_port, + 'db_user': get.db_user, + 'db_password': get.db_password, + 'ps': public.xssencode2(get.db_ps.strip()) + } + + result = public.M("database_servers").where('id=?', (id,)).update(pdata) + if isinstance(result, int): + public.write_log_gettext('Database manager', 'Edit remote MySQL server [{}:{}]',(get.db_host, get.db_port)) + return public.return_msg_gettext(True, 'Setup successfully!') + return public.return_msg_gettext(False, 'Fail to edit: {}',(result)) + + def AddCloudDatabase(self, get): + ''' + @name 添加远程数据库 + @author hwliang<2022-01-06> + @param db_host 服务器地址 + @param db_port 数据库端口 + @param db_user 用户名 + @param db_name 数据库名称 + @param db_password 数据库密码 + @param db_ps 数据库备注 + @return dict + ''' + if not hasattr(get, 'db_host'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + if not hasattr(get, 'db_port'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + if not hasattr(get, 'db_user'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + if not hasattr(get, 'db_name'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + if not hasattr(get, 'db_password'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + if not hasattr(get, 'db_ps'): + return public.return_msg_gettext(False, 'Parameter ERROR!') + + # 检查数据库是否能连接 + res = self.CheckCloudDatabase(get) + if isinstance(res, dict): return res + + if public.M('databases').where('name=?', (get.db_name,)).count(): + return public.return_msg_gettext(False, "A database with the same name already exists: [{}]",(get.db_name,)) + get.db_port = int(get.db_port) + conn_config = { + 'db_host': get.db_host, + 'db_port': get.db_port, + 'db_user': get.db_user, + 'db_password': get.db_password, + 'db_name': get.db_name + } + + pdata = { + 'name': get.db_name, + 'ps': get.db_ps, + 'conn_config': json.dumps(conn_config), + 'db_type': '1', + 'username': get.db_user, + 'password': get.db_password, + 'accept': '127.0.0.1', + 'addtime': time.strftime('%Y-%m-%d %X', time.localtime()), + 'pid': 0 + } + + result = public.M('databases').insert(pdata) + if isinstance(result, int): + public.write_log_gettext('Database manager', 'Add remote MySQL database [{}] successfully',(get.db_name,)) + return public.return_msg_gettext(True, 'Setup successfully!') + return public.return_msg_gettext(False, 'Add failed: {}',(result,)) + + def CheckCloudDatabase(self, conn_config): + ''' + @name 检查远程数据库信息是否正确 + @author hwliang<2022-01-06> + @param conn_config 连接信息 + db_host 服务器地址 + db_port 数据库端口 + db_user 用户名 + db_name 数据库名称 + db_password 数据库密码 + @return True / dict + ''' + try: + import db_mysql + if not 'db_name' in conn_config: conn_config['db_name'] = None + mysql_obj = db_mysql.panelMysql().set_host(conn_config['db_host'], conn_config['db_port'], + conn_config['db_name'], conn_config['db_user'], + conn_config['db_password']) + result = mysql_obj.query("show databases") + if not conn_config['db_name']: return True + for i in result: + if i[0] == conn_config['db_name']: + return True + return public.returnMsg(False, 'The specified database does not exist!') + except Exception as ex: + res = self.GetMySQLError(ex) + if not res: res = str(ex) + return public.returnMsg(False, res) + + def GetMySQLError(self, e): + res = '' + if e.args[0] == 1045: + res = public.gettext_msg("Wrong user name or password!") + if e.args[0] == 1049: + res = public.gettext_msg("Database does NOT exist!") + if e.args[0] == 1044: + res = public.gettext_msg("No access rights, or the database does not exist!") + if e.args[0] == 1062: + res = public.gettext_msg("Database exists!") + if e.args[0] == 1146: + res = public.gettext_msg('Database table does not exist!') + if e.args[0] == 2003: + res = public.gettext_msg('Fail to connect to the server!') + if res: + res = res + "
                                      " + str(e) + "
                                      " + else: + res = str(e) + return res # 检查mysql是否存在空用户密码 def _check_empty_user_passwd(self): @@ -23,75 +263,79 @@ class database(datatool.datatools): #添加数据库 def AddDatabase(self,get): - try: - self._check_empty_user_passwd() - ssl = "" - if hasattr(get,"ssl"): - ssl = get.ssl - if ssl == "REQUIRE SSL" and not self.check_mysql_ssl_status(get): - return public.returnMsg(False,'MYSQL_SSL_ERR') - data_name = get['name'].strip().lower() - if not data_name: return public.returnMsg(False, 'The database name cannot be empty') - if self.CheckRecycleBin(data_name): return public.returnMsg(False,'DATABASE_DEL_RECYCLE_BIN',(data_name,)) - if len(data_name) > 64: return public.returnMsg(False, 'DATABASE_NAME_LEN') - reg = r"^[\w\.-]+$" - username = get.db_user.strip() - if not username: return public.returnMsg(False,'The database user name cannot be empty') - if not re.match(reg, data_name): return public.returnMsg(False,'DATABASE_NAME_ERR_T') - if not re.match(reg, username): return public.returnMsg(False,'DATABASE_NAME_ERR') - if not hasattr(get,'db_user'): get.db_user = data_name + # try: + self._check_empty_user_passwd() + ssl = "" + if hasattr(get,"ssl"): + ssl = get.ssl + if ssl == "REQUIRE SSL" and not self.check_mysql_ssl_status(get): + return public.return_msg_gettext(False,'SSL is not enabled in the database, please open it in the Mysql manager first') + data_name = get['name'].strip().lower() + if not data_name: return public.return_msg_gettext(False, 'The database name cannot be empty') + if self.CheckRecycleBin(data_name): return public.return_msg_gettext(False,'Database [{}] already at the recycle bin, please recover from the recycle bin!',(data_name,)) + if len(data_name) > 64: return public.return_msg_gettext(False, 'Database name cannot be more than 16 characters!') + reg = r"^[\w\.-]+$" + username = get.db_user.strip() + if not username: return public.return_msg_gettext(False, 'The database user name cannot be empty') + if not re.match(reg, data_name): return public.return_msg_gettext(False,'Database name cannot contain special characters!') + if not re.match(reg, username): return public.return_msg_gettext(False,'Database name is illegal!') + if not hasattr(get,'db_user'): get.db_user = data_name - 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') - data_pwd = get['password'] - if len(data_pwd)<1: - data_pwd = public.md5(str(time.time()))[0:8] - - sql = public.M('databases') - if sql.where("name=?",(data_name)).count(): return public.returnMsg(False,'DATABASE_NAME_EXISTS') - if sql.where("username=?", (username)).count(): return public.returnMsg(False, 'DATABASE_USERNAME_EXISTS') - address = get['address'].strip() - if address in ['','ip']: return public.returnMsg(False,'If the access permission is [Specified IP], you need to enter the IP address!') + checks = ['root','mysql','test','sys','panel_logs'] + if username in checks or len(username) < 1: return public.return_msg_gettext(False,'Database username is illegal!') + if data_name in checks or len(data_name) < 1: return public.return_msg_gettext(False,'Database name is illegal!') + data_pwd = get['password'] + if len(data_pwd)<1: + data_pwd = public.md5(str(time.time()))[0:16] - user = '是' - password = data_pwd - - codeing = get['codeing'] - - wheres={ - 'utf8' : 'utf8_general_ci', - 'utf8mb4' : 'utf8mb4_general_ci', - 'gbk' : 'gbk_chinese_ci', - 'big5' : 'big5_chinese_ci' - } - codeStr=wheres[codeing] - #添加MYSQL - mysql_obj = panelMysql.panelMysql() - #从MySQL验证是否存在 - if self.database_exists_for_mysql(mysql_obj,data_name): return public.returnMsg(False,'DB_EXIST1') + sql = public.M('databases') + if sql.where("name=?",(data_name)).count(): return public.return_msg_gettext(False,'Database exists!') + if sql.where("username=?", (username)).count(): return public.return_msg_gettext(False, 'The user name already exists. For security reasons, we do not allow one database user to manage multiple databases') + address = get['address'].strip() + if address in ['','ip']: return public.return_msg_gettext(False,'If the access permission is [Specified IP], you need to enter the IP address!') - result = mysql_obj.execute("create database `" + data_name + "` DEFAULT CHARACTER SET " + codeing + " COLLATE " + codeStr) - isError = self.IsSqlError(result) - if isError != None: return isError - mysql_obj.execute("drop user '" + username + "'@'localhost'") - for a in address.split(','): - mysql_obj.execute("drop user '" + username + "'@'" + a + "'") - self.__CreateUsers(data_name,username,password,address,ssl) - - if get['ps'] == '': get['ps']=public.getMsg('INPUT_PS') - get['ps'] = public.xssencode(get['ps']) - addTime = time.strftime('%Y-%m-%d %X',time.localtime()) - - pid = 0 - if hasattr(get,'pid'): pid = get.pid - #添加入SQLITE - sql.add('pid,name,username,password,accept,ps,addtime',(pid,data_name,username,password,address,get['ps'],addTime)) - public.WriteLog("TYPE_DATABASE", 'DATABASE_ADD_SUCCESS',(data_name,)) - return public.returnMsg(True,'ADD_SUCCESS') - except Exception as ex: - public.WriteLog("TYPE_DATABASE",'DATABASE_ADD_ERR', (data_name,str(ex))) - return public.returnMsg(False,'ADD_ERROR') + user = '是' + password = data_pwd + + codeing = get['codeing'] + + wheres={ + 'utf8' : 'utf8_general_ci', + 'utf8mb4' : 'utf8mb4_general_ci', + 'gbk' : 'gbk_chinese_ci', + 'big5' : 'big5_chinese_ci' + } + codeStr=wheres[codeing] + #添加MYSQL + self.sid = get.get('sid/d',0) + mysql_obj = public.get_mysql_obj_by_sid(self.sid) + + #从MySQL验证是否存在 + if self.database_exists_for_mysql(mysql_obj,data_name): return public.return_msg_gettext(False,'The specified database already exists in MySQL, please change the name!') + + result = mysql_obj.execute("create database `" + data_name + "` DEFAULT CHARACTER SET " + codeing + " COLLATE " + codeStr) + isError = self.IsSqlError(result) + if isError != None: return isError + mysql_obj.execute("drop user '" + username + "'@'localhost'") + for a in address.split(','): + mysql_obj.execute("drop user '" + username + "'@'" + a + "'") + self.__CreateUsers(data_name,username,password,address,ssl) + + if get['ps'] == '': get['ps']=public.get_msg_gettext('Edit notes') + get['ps'] = public.xssencode2(get['ps']) + addTime = time.strftime('%Y-%m-%d %X',time.localtime()) + + pid = 0 + if hasattr(get,'pid'): pid = get.pid + #添加入SQLITE + db_type = 0 + if self.sid: db_type = 2 + sql.add('pid,sid,db_type,name,username,password,accept,ps,addtime',(pid,self.sid,db_type,data_name,username,password,address,get['ps'],addTime)) + public.write_log_gettext("Database manager", 'Successfully added database [{}]!',(data_name,)) + return public.return_msg_gettext(True,'Setup successfully!') + # except Exception as ex: + # public.write_log_gettext("Database manager",'Failed to add database [{}]!, {}', (data_name,str(ex))) + # return public.return_msg_gettext(False,'Failed to add') # 生成mysql证书 def _create_mysql_ssl(self): @@ -125,14 +369,14 @@ ssl-key=/www/server/mysql/mysql-test/std_data/server-key.pem conf_file = "/etc/my.cnf" conf = public.readFile(conf_file) if not conf: - return public.returnMsg(False,"CONF_FILE_NOT_EXISTS") + return public.return_msg_gettext(False,'Configuration file not exist') if self.check_mysql_ssl_status(get): reg = "ssl-ca=/www.*\n.*\n.*server-key.pem\n" conf = re.sub(reg,"",conf) if os.path.exists('/www/server/mysql/mysql-test/std_data/server-cert.pem'): conf = re.sub('\[mysqld\]', '[mysqld]\nskip_ssl', conf) public.writeFile(conf_file,conf) - return public.returnMsg(True,"SET_SUCCESS") + return public.return_msg_gettext(True,'Setup successfully!') # create_ssl = None # for i in ['5.5','5.6','10.1','10.2','10.3']: # if i not in public.readFile('/www/server/mysql/version_check.pl'): @@ -145,7 +389,7 @@ ssl-key=/www/server/mysql/mysql-test/std_data/server-key.pem conf = re.sub('skip_ssl\n', '', conf) public.writeFile(conf_file,conf) # public.ExecShell('chown mysql.mysql /www/server/data/*.pem') - return public.returnMsg(True,"MYSQL_SSL_OPEN_SUCCESS") + return public.return_msg_gettext(True,'Open successfully, take effect after manually restarting the database') # 检查mysqlssl状态 def check_mysql_ssl_status(self,get): @@ -174,21 +418,25 @@ ssl-key=/www/server/mysql/mysql-test/std_data/server-key.pem #创建用户 def __CreateUsers(self,dbname,username,password,address,ssl=None): - mysql_obj = panelMysql.panelMysql() + mysql_obj = public.get_mysql_obj_by_sid(self.sid) mysql_obj.execute("CREATE USER `%s`@`localhost` IDENTIFIED BY '%s'" % (username,password)) - mysql_obj.execute("grant all privileges on `%s`.* to `%s`@`localhost` %s" % (dbname,username,ssl)) + result = mysql_obj.execute("grant all privileges on `%s`.* to `%s`@`localhost`" % (dbname,username)) + if str(result).find('1044') != -1: + mysql_obj.execute("grant SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,INDEX,ALTER,CREATE TEMPORARY TABLES,LOCK TABLES,EXECUTE,CREATE VIEW,SHOW VIEW,EVENT,TRIGGER on `%s`.* to `%s`@`localhost`" % (dbname,username)) if not ssl: mysql_obj.execute("update mysql.user set ssl_type='' where user='%s' and host='localhost'" % (username)) for a in address.split(','): mysql_obj.execute("CREATE USER `%s`@`%s` IDENTIFIED BY '%s'" % (username,a,password)) - mysql_obj.execute("grant all privileges on `%s`.* to `%s`@`%s` %s" % (dbname,username,a,ssl)) + result = mysql_obj.execute("grant all privileges on `%s`.* to `%s`@`%s`" % (dbname,username,a)) + if str(result).find('1044') != -1: + mysql_obj.execute("grant SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,INDEX,ALTER,CREATE TEMPORARY TABLES,LOCK TABLES,EXECUTE,CREATE VIEW,SHOW VIEW,EVENT,TRIGGER on `%s`.* to `%s`@`%s` %s" % (dbname,username,a,ssl)) mysql_obj.execute("flush privileges") #检查是否在回收站 def CheckRecycleBin(self,name): try: u_name = self.db_name_to_unicode(name) - for n in os.listdir('/www/Recycle_bin'): + for n in os.listdir('/www/.Recycle_bin'): if n.find('BTDB_'+name+'_t_') != -1: return True if n.find('BTDB_'+u_name+'_t_') != -1: return True return False @@ -198,17 +446,17 @@ ssl-key=/www/server/mysql/mysql-test/std_data/server-key.pem #检测数据库执行错误 def IsSqlError(self,mysqlMsg): mysqlMsg=str(mysqlMsg) - if "MySQLdb" in mysqlMsg: return public.returnMsg(False,'DATABASE_ERR_MYSQLDB') - if "2002," in mysqlMsg or '2003,' in mysqlMsg: return public.returnMsg(False,'DATABASE_ERR_CONNECT') - if "using password:" in mysqlMsg: return public.returnMsg(False,'DATABASE_ERR_PASS') - if "Connection refused" in mysqlMsg: return public.returnMsg(False,'DATABASE_ERR_CONNECT') - if "1133" in mysqlMsg: return public.returnMsg(False,'DATABASE_ERR_NOT_EXISTS') + if "MySQLdb" in mysqlMsg: return public.return_msg_gettext(False,'MySQLdb component is missing!
                                      Please enter SSH and run the command: pip install mysql-python') + if "2002," in mysqlMsg or '2003,' in mysqlMsg: return public.return_msg_gettext(False,'ERROR to connect database, pls check database status!') + if "using password:" in mysqlMsg: return public.return_msg_gettext(False,'Mysql root or user password is incorrect, please try to reset!') + if "Connection refused" in mysqlMsg: return public.return_msg_gettext(False,'ERROR to connect database, pls check database status!') + if "1133" in mysqlMsg: return public.return_msg_gettext(False,'Database user does NOT exist!') if "libmysqlclient" in mysqlMsg: self.rep_lnk() public.ExecShell("pip uninstall mysql-python -y") public.ExecShell("pip install pymysql") public.writeFile('data/restart.pl','True') - return public.returnMsg(False,"MYSQL_FIX_WITH_AUTO_ERR") + return public.return_msg_gettext(False,'Execution failed, attempted auto repair, please try again later!') return None def rep_lnk(self): @@ -320,33 +568,32 @@ SetLink #删除数据库 def DeleteDatabase(self,get): - try: - id=get['id'] - name = get['name'] - if os.path.exists('data/recycle_bin_db.pl'): return self.DeleteToRecycleBin(name) - - find = public.M('databases').where("id=?",(id,)).field('id,pid,name,username,password,accept,ps,addtime').find() + # try: + id=get['id'] + name = get['name'] + find = public.M('databases').where("id=?",(id,)).field('id,sid,pid,name,username,password,accept,ps,addtime,db_type').find() + self.sid = find['sid'] + if find['db_type'] in ['0',0] or self.sid: # 删除本地数据库 + if os.path.exists('data/recycle_bin_db.pl') and not self.sid: return self.DeleteToRecycleBin(name) accept = find['accept'] username = find['username'] - if "'" in username: - username=re.sub("\'","\\'",username) #删除MYSQL - result = panelMysql.panelMysql().execute("drop database `" + name + "`") + mysql_obj = public.get_mysql_obj_by_sid(self.sid) + result = mysql_obj.execute("drop database `" + name + "`") isError=self.IsSqlError(result) if isError != None: return isError - users = panelMysql.panelMysql().query("select Host from mysql.user where User='" + username + "' AND Host!='localhost'") - panelMysql.panelMysql().execute("drop user '" + username + "'@'localhost'") + users = mysql_obj.query("select Host from mysql.user where User='" + username + "' AND Host!='localhost'") + mysql_obj.execute("drop user '" + username + "'@'localhost'") for us in users: - panelMysql.panelMysql().execute("drop user '" + username + "'@'" + us[0] + "'") - panelMysql.panelMysql().execute("flush privileges") - #删除SQLITE - public.M('databases').where("id=?",(id,)).delete() - public.WriteLog("TYPE_DATABASE", 'DATABASE_DEL_SUCCESS',(name,)) - return public.returnMsg(True, 'DEL_SUCCESS') - except Exception as ex: - public.WriteLog("TYPE_DATABASE",'DATABASE_DEL_ERR',(get.name , str(ex))) - return public.returnMsg(False,'DEL_ERROR') - + mysql_obj.execute("drop user '" + username + "'@'" + us[0] + "'") + mysql_obj.execute("flush privileges") + #删除SQLITE + public.M('databases').where("id=?",(id,)).delete() + public.write_log_gettext("Database manager", 'Successfully deleted database [{}]!',(name,)) + return public.return_msg_gettext(True, 'Successfully deleted') + # except Exception as ex: + # public.write_log_gettext("Database manager",'Failed to delete database [{}]!, {}',(get.name , str(ex))) + # return public.return_msg_gettext(False,'Failed to delete') def db_name_to_unicode(self,name): ''' @@ -356,6 +603,7 @@ SetLink @return name Unicode编码的数据库名 ''' name = name.replace('.','@002e') + name = name.replace('-','@002d') return name.encode("unicode_escape").replace(b"\\u",b"@").decode() #删除数据库到回收站 @@ -365,10 +613,15 @@ SetLink username = data['username'] panelMysql.panelMysql().execute("drop user '" + username + "'@'localhost'") users = panelMysql.panelMysql().query("select Host from mysql.user where User='" + username + "' AND Host!='localhost'") - for us in users: - panelMysql.panelMysql().execute("drop user '" + username + "'@'" + us[0] + "'") + if isinstance(users,str): + return public.return_msg_gettext(False,'Delete failed, failed to connect to database!') + try: + for us in users: + panelMysql.panelMysql().execute("drop user '" + username + "'@'" + us[0] + "'") + except Exception: + pass panelMysql.panelMysql().execute("flush privileges") - rPath = '/www/Recycle_bin/' + rPath = '/www/.Recycle_bin/' data['rmtime'] = int(time.time()) u_name = self.db_name_to_unicode(name) rm_path = '{}/BTDB_{}_t_{}'.format(rPath,u_name,data['rmtime']) @@ -378,16 +631,16 @@ SetLink db_path = '{}/{}'.format(datadir,u_name) if not os.path.exists(db_path): - return public.returnMsg(False,'Means that the database data does not exist!') + return public.return_msg_gettext(False,'The database data does not exist!') public.ExecShell("mv -f {} {}".format(db_path,rm_path)) if not os.path.exists(rm_path): - return public.returnMsg(False,'Failed to move database data to the recycle bin!') + return public.return_msg_gettext(False,'Failed to move database data to the recycle bin!') public.writeFile(rm_config_file,json.dumps(data)) # public.writeFile(rPath + 'BTDB_' + name +'_t_' + str(time.time()),json.dumps(data)) public.M('databases').where("name=?",(name,)).delete() - public.WriteLog("TYPE_DATABASE", 'DATABASE_DEL_SUCCESS',(name,)) - return public.returnMsg(True,'RECYCLE_BIN_DB') + public.write_log_gettext("Database manager", 'Successfully deleted database [{}]!',(name,)) + return public.return_msg_gettext(True,'Database moved to recycle bin!') #永久删除数据库 def DeleteTo(self,filename): @@ -396,7 +649,7 @@ SetLink data = json.loads(public.readFile(filename)) if public.M('databases').where("name=?",( data['name'],)).count(): os.remove(filename) - return public.returnMsg(True,'DEL_SUCCESS') + return public.return_msg_gettext(True,'Successfully deleted') result = panelMysql.panelMysql().execute("drop database `" + data['name'] + "`") isError=self.IsSqlError(result) if isError != None: return isError @@ -412,10 +665,10 @@ SetLink data = json.loads(public.readFile(filename + '/config.json')) shutil.rmtree(filename) try: - public.WriteLog("TYPE_DATABASE", 'DATABASE_DEL_SUCCESS',(data['name'],)) + public.write_log_gettext("Database manager", 'Successfully deleted database [{}]!',(data['name'],)) except: pass - return public.returnMsg(True,'DEL_SUCCESS') + return public.return_msg_gettext(True,'Successfully deleted') #恢复数据库 def RecycleDB(self,filename): @@ -429,12 +682,12 @@ SetLink u_name = self.db_name_to_unicode(data['name']) db_path = "{}/{}".format(public.get_datadir(),u_name) if os.path.exists(db_path): - return public.returnMsg(False,'There is a database with the same name in the current database. To ensure data security, stop recovery!') + return public.return_msg_gettext(False,'There is a database with the same name in the current database. To ensure data security, stop recovery!') _isdir = True if public.M('databases').where("name=?",( data['name'],)).count(): if not _isdir: os.remove(filename) - return public.returnMsg(True,'RECYCLEDB') + return public.return_msg_gettext(True,'Database recovered!') if not _isdir: @@ -442,7 +695,7 @@ SetLink else: public.ExecShell('mv -f {} {}'.format(filename,db_path)) if not os.path.exists(db_path): - return public.returnMsg(False,'Data recovery failed!') + return public.return_msg_gettext(False,'Data recovery failed!') db_config_file = "{}/config.json".format(db_path) if os.path.exists(db_config_file): os.remove(db_config_file) @@ -453,22 +706,22 @@ SetLink self.__CreateUsers(data['name'],data['username'],data['password'],data['accept']) public.M('databases').add('id,pid,name,username,password,accept,ps,addtime',(data['id'],data['pid'],data['name'],data['username'],data['password'],data['accept'],data['ps'],data['addtime'])) - return public.returnMsg(True,"RECYCLEDB") + return public.return_msg_gettext(True,'Database recovered!') #设置ROOT密码 def SetupPassword(self,get): password = get['password'].strip() try: - if not password: return public.returnMsg(False,'MYSQL_ROOT_PASSWD_EMTPY_ERR') + if not password: return public.return_msg_gettext(False,'Root password cannot be empty') 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') + if not re.match(rep, password): return public.return_msg_gettext(False, 'Database password cannot contain special characters!') + self.sid = get.get('sid/d',0) #修改MYSQL - mysql_obj = panelMysql.panelMysql() + mysql_obj = public.get_mysql_obj_by_sid(self.sid) result = mysql_obj.query("show databases") isError=self.IsSqlError(result) is_modify = True - if isError != None: + if isError != None and not self.sid: #尝试使用新密码 public.M('config').where("id=?",(1,)).setField('mysql_root',password) result = mysql_obj.query("show databases") @@ -476,114 +729,155 @@ SetLink if isError != None: public.ExecShell("cd /www/server/panel && "+public.get_python_bin()+" tools.py root \"" + password + "\"") is_modify = False + if is_modify: + admin_user = 'root' m_version = public.readFile(public.GetConfigValue('setup_path') + '/mysql/version.pl') - + if self.sid: + admin_user = mysql_obj._USER + m_version = mysql_obj.query('select version();')[0][0] + if m_version.find('5.7') == 0 or m_version.find('8.0') == 0: - accept = self.map_to_list(mysql_obj.query("select Host from mysql.user where User='root'")) + accept = self.map_to_list(mysql_obj.query("select Host from mysql.user where User='{}'".format(admin_user))) for my_host in accept: - mysql_obj.execute("UPDATE mysql.user SET authentication_string='' WHERE User='root' and Host='{}'".format(my_host[0])) - mysql_obj.execute("ALTER USER `%s`@`%s` IDENTIFIED BY '%s'" % ('root',my_host[0],password)) + mysql_obj.execute("UPDATE mysql.user SET authentication_string='' WHERE User='{}' and Host='{}'".format(admin_user,my_host[0])) + mysql_obj.execute("ALTER USER `%s`@`%s` IDENTIFIED BY '%s'" % (admin_user,my_host[0],password)) elif m_version.find('10.5.') != -1 or m_version.find('10.4.') != -1: - accept = self.map_to_list(mysql_obj.query("select Host from mysql.user where User='root'")) + accept = self.map_to_list(mysql_obj.query("select Host from mysql.user where User='{}'".format(admin_user))) for my_host in accept: - mysql_obj.execute("ALTER USER `%s`@`%s` IDENTIFIED BY '%s'" % ('root',my_host[0],password)) + mysql_obj.execute("ALTER USER `%s`@`%s` IDENTIFIED BY '%s'" % (admin_user,my_host[0],password)) else: - result = mysql_obj.execute("update mysql.user set Password=password('" + password + "') where User='root'") + result = mysql_obj.execute("update mysql.user set Password=password('" + password + "') where User='{}'".format(admin_user)) mysql_obj.execute("flush privileges") - msg = public.getMsg('DATABASE_ROOT_SUCCESS') + msg = public.get_msg_gettext('Successfully modified root password!') #修改SQLITE - public.M('config').where("id=?",(1,)).setField('mysql_root',password) - public.WriteLog("TYPE_DATABASE", "DATABASE_ROOT_SUCCESS") - session['config']['mysql_root']=password - return public.returnMsg(True,msg) + if self.sid: + public.M('database_servers').where('id=?',self.sid).setField('db_password',password) + public.write_log_gettext("TYPE_DATABASE", "Change the password of the remote MySQL server") + else: + public.M('config').where("id=?",(1,)).setField('mysql_root',password) + public.write_log_gettext("Database manager", 'Successfully modified root password!') + session['config']['mysql_root']=password + return public.return_msg_gettext(True,msg) except Exception as ex: - return public.returnMsg(False,'EDIT_ERROR' + str(ex)) + return public.return_msg_gettext(False,'Failed to modify ' + str(ex)) #修改用户密码 def ResDatabasePassword(self,get): - try: - newpassword = get['password'] - username = get['name'] - id = get['id'] - if not newpassword: return public.returnMsg(False,'DB_PASSWD_EMPTY_ERR', (username,)) - name = public.M('databases').where('id=?',(id,)).getField('name') - - rep = "^[\w@\.\?\-\_\>\<\~\!\#\$\%\^\&\*\(\)]+$" - if not re.match(rep, newpassword): return public.returnMsg(False, 'DATABASE_NAME_ERR_T') - #修改MYSQL - mysql_obj = panelMysql.panelMysql() - m_version = public.readFile(public.GetConfigValue('setup_path') + '/mysql/version.pl') - if m_version.find('5.7') == 0 or m_version.find('8.0') == 0 : - accept = self.map_to_list(panelMysql.panelMysql().query("select Host from mysql.user where User='" + name + "' AND Host!='localhost'")) - mysql_obj.execute("update mysql.user set authentication_string='' where User='" + username + "'") - result = mysql_obj.execute("ALTER USER `%s`@`localhost` IDENTIFIED BY '%s'" % (username,newpassword)) - for my_host in accept: - mysql_obj.execute("ALTER USER `%s`@`%s` IDENTIFIED BY '%s'" % (username,my_host[0],newpassword)) - elif m_version.find('10.5.') != -1 or m_version.find('10.4.') != -1: - accept = self.map_to_list(panelMysql.panelMysql().query("select Host from mysql.user where User='" + name + "' AND Host!='localhost'")) - result = mysql_obj.execute("ALTER USER `%s`@`localhost` IDENTIFIED BY '%s'" % (username,newpassword)) - for my_host in accept: - mysql_obj.execute("ALTER USER `%s`@`%s` IDENTIFIED BY '%s'" % (username,my_host[0],newpassword)) - else: - result = mysql_obj.execute("update mysql.user set Password=password('" + newpassword + "') where User='" + username + "'") - - isError=self.IsSqlError(result) - if isError != None: return isError + # try: + newpassword = get['password'] + username = get['name'] + id = get['id'] + if not newpassword: return public.return_msg_gettext(False,'Database [{}] password cannot be empty', (username,)) + db_find = public.M('databases').where('id=?',(id,)).find() + name = db_find['name'] - mysql_obj.execute("flush privileges") - #if result==False: return public.returnMsg(False,'DATABASE_PASS_ERR_NOT_EXISTS') - #修改SQLITE - if int(id) > 0: - public.M('databases').where("id=?",(id,)).setField('password',newpassword) - else: - public.M('config').where("id=?",(id,)).setField('mysql_root',newpassword) - session['config']['mysql_root'] = newpassword - - public.WriteLog("TYPE_DATABASE",'DATABASE_PASS_SUCCESS',(name,)) - return public.returnMsg(True,'DATABASE_PASS_SUCCESS',(name,)) - except Exception as ex: - import traceback - public.WriteLog("TYPE_DATABASE", 'DATABASE_PASS_ERROR',(username,traceback.format_exc(limit=True).replace('\n','
                                      '))) - return public.returnMsg(False,'DATABASE_PASS_ERROR',(name,)) + rep = "^[\w@\.\?\-\_\>\<\~\!\#\$\%\^\&\*\(\)]+$" + if not re.match(rep, newpassword): return public.return_msg_gettext(False, 'Database password cannot contain special characters!') + #修改MYSQL + self.sid = db_find['sid'] + if self.sid and username == 'root': return public.returnMsg(False,'Cannot change the root password of the remote database') + mysql_obj = public.get_mysql_obj_by_sid(self.sid) + m_version = public.readFile(public.GetConfigValue('setup_path') + '/mysql/version.pl') + if self.sid: + m_version = mysql_obj.query('select version();')[0][0] + + if m_version.find('5.7') == 0 or m_version.find('8.0') == 0 : + accept = self.map_to_list(mysql_obj.query("select Host from mysql.user where User='" + name + "' AND Host!='localhost'")) + mysql_obj.execute("update mysql.user set authentication_string='' where User='" + username + "'") + result = mysql_obj.execute("ALTER USER `%s`@`localhost` IDENTIFIED BY '%s'" % (username,newpassword)) + for my_host in accept: + mysql_obj.execute("ALTER USER `%s`@`%s` IDENTIFIED BY '%s'" % (username,my_host[0],newpassword)) + elif m_version.find('10.5.') != -1 or m_version.find('10.4.') != -1: + accept = self.map_to_list(mysql_obj.query("select Host from mysql.user where User='" + name + "' AND Host!='localhost'")) + result = mysql_obj.execute("ALTER USER `%s`@`localhost` IDENTIFIED BY '%s'" % (username,newpassword)) + for my_host in accept: + mysql_obj.execute("ALTER USER `%s`@`%s` IDENTIFIED BY '%s'" % (username,my_host[0],newpassword)) + else: + result = mysql_obj.execute("update mysql.user set Password=password('" + newpassword + "') where User='" + username + "'") + + isError=self.IsSqlError(result) + if isError != None: return isError + + mysql_obj.execute("flush privileges") + #if result==False: return public.return_msg_gettext(False,'Failed to modify, database user does not exist!') + #修改SQLITE + if int(id) > 0: + public.M('databases').where("id=?",(id,)).setField('password',newpassword) + else: + public.M('config').where("id=?",(id,)).setField('mysql_root',newpassword) + session['config']['mysql_root'] = newpassword + + public.write_log_gettext("Database manager",'Successfully modifyied password for database [{}]!',(name,)) + return public.return_msg_gettext(True,'Successfully modifyied password for database [{}]!',(name,)) + # except Exception as ex: + # import traceback + # public.write_log_gettext("Database manager", 'Failed to modify password for database [{}]!',(username,traceback.format_exc(limit=True).replace('\n','
                                      '))) + # return public.return_msg_gettext(False,'Failed to modify password for database [{}]!',(name,)) #备份 def ToBackup(self,get): #try: - result = panelMysql.panelMysql().execute("show databases") - isError=self.IsSqlError(result) - if isError: return isError id = get['id'] - 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'): public.ExecShell('mkdir -p ' + session['config']['backup_path'] + '/database') - if not self.mypass(True, root):return public.returnMsg(False, 'MYSQL_CONF_ERR') - + db_find = public.M('databases').where("id=?",(id,)).find() + name = db_find['name'] fileName = name + '_' + time.strftime('%Y%m%d_%H%M%S',time.localtime()) + '.sql.gz' backupName = session['config']['backup_path'] + '/database/' + fileName + mysqldump_bin = public.get_mysqldump_bin() + if db_find['db_type'] in ['0',0]: + # 本地数据库 + result = panelMysql.panelMysql().execute("show databases") + isError=self.IsSqlError(result) + if isError: return isError - try: - password = public.M('config').where('id=?',(1,)).getField('mysql_root') - os.environ["MYSQL_PWD"] = password - public.ExecShell("/www/server/mysql/bin/mysqldump -R -E --triggers=false --default-character-set="+ public.get_database_character(name) +" --force --opt \"" + name + "\" -u root | gzip > " + backupName) - except Exception as e: - raise - finally: - os.environ["MYSQL_PWD"] = "" + root = public.M('config').where('id=?',(1,)).getField('mysql_root') + if not os.path.exists(session['config']['backup_path'] + '/database'): public.ExecShell('mkdir -p ' + session['config']['backup_path'] + '/database') + if not self.mypass(True, root):return public.return_msg_gettext(False, 'Database configuration file failed to get checked, please check if MySQL configuration file exists [/etc/my.cnf]') + try: + password = public.M('config').where('id=?',(1,)).getField('mysql_root') + os.environ["MYSQL_PWD"] = str(password) + public.ExecShell(mysqldump_bin + " -R -E --triggers=false --default-character-set="+ public.get_database_character(name) +" --force --opt \"" + name + "\" -u root -p"+str(password)+" | gzip > " + backupName) + except Exception as e: + raise + finally: + os.environ["MYSQL_PWD"] = "" + self.mypass(False, root) + elif db_find['db_type'] in ['1', 1]: + # 远程数据库 + try: + conn_config = json.loads(db_find['conn_config']) + res = self.CheckCloudDatabase(conn_config) + if isinstance(res,dict): return res + os.environ["MYSQL_PWD"] = str(conn_config['db_password']) + public.ExecShell(mysqldump_bin + " -h "+ conn_config['db_host'] +" -P "+ str(int(conn_config['db_port'])) +" -R -E --triggers=false --default-character-set=" + public.get_database_character(name) + " --force --opt \"" + str(db_find['name']) + "\" -u "+ str(conn_config['db_user']) +" -p"+str(conn_config['db_password'])+" | gzip > " + backupName) + except Exception as e: + raise + finally: + os.environ["MYSQL_PWD"] = "" + elif db_find['db_type'] in ['2', 2]: + try: + conn_config = public.M('database_servers').where('id=?', db_find['sid']).find() + res = self.CheckCloudDatabase(conn_config) + if isinstance(res,dict): return res + os.environ["MYSQL_PWD"] = str(conn_config['db_password']) + public.ExecShell(mysqldump_bin + " -h "+ conn_config['db_host'] +" -P "+ str(int(conn_config['db_port'])) +" -R -E --triggers=false --default-character-set=" + public.get_database_character(name) + " --force --opt \"" + str(db_find['name']) + "\" -u "+ str(conn_config['db_user']) +" -p"+str(conn_config['db_password'])+" | gzip > " + backupName) + except Exception as e: + raise + finally: + os.environ["MYSQL_PWD"] = "" + else: + return public.return_msg_gettext(False, 'Unsupported database type') - if not os.path.exists(backupName): return public.returnMsg(False,'BACKUP_ERROR') - - self.mypass(False, root) - + if not os.path.exists(backupName): return public.return_msg_gettext(False, 'Backup error!') sql = public.M('backup') addTime = time.strftime('%Y-%m-%d %X',time.localtime()) sql.add('type,name,pid,filename,size,addtime',(1,fileName,id,backupName,0,addTime)) - public.WriteLog("TYPE_DATABASE", "DATABASE_BACKUP_SUCCESS",(name,)) - return public.returnMsg(True, 'BACKUP_SUCCESS') + public.write_log_gettext("Database manager", "Backup database [{}] succeed!",(name,)) + return public.return_msg_gettext(True, 'Backup Succeeded!') #except Exception as ex: - #public.WriteLog("数据库管理", "备份数据库[" + name + "]失败 => " + str(ex)) - #return public.returnMsg(False,'备份失败!') + #public.write_log_gettext("数据库管理", "备份数据库[" + name + "]失败 => " + str(ex)) + #return public.return_msg_gettext(False,'备份失败!') #删除备份文件 def DelBackup(self,get): @@ -598,18 +892,16 @@ SetLink public.ExecShell(public.get_python_bin() + " "+public.GetConfigValue('setup_path') + '/panel/script/backup_qiniu.py delete_file ' + name) public.M('backup').where(where,(id,)).delete() - public.WriteLog("TYPE_DATABASE", 'DATABASE_BACKUP_DEL_SUCCESS',(name,filename)) - return public.returnMsg(True, 'DEL_SUCCESS') + public.write_log_gettext("Database manager", 'Successfully deleted backup [{}] for database [{}]!',(name,filename)) + return public.return_msg_gettext(True, 'Successfully deleted') except Exception as ex: - public.WriteLog("TYPE_DATABASE", 'DATABASE_BACKUP_DEL_ERR',(name,filename,str(ex))) - return public.returnMsg(False,'DEL_ERROR') + public.write_log_gettext("Database manager", 'Failed to delete backup [{}] for database [{}]! => {}',(name,filename,str(ex))) + return public.return_msg_gettext(False,'Failed to delete') #导入 def InputSql(self, get): # try: - result = panelMysql.panelMysql().execute("show databases") - isError = self.IsSqlError(result) - if isError: return isError + name = get['name'] file = get['file'] if "|" in file: @@ -619,8 +911,14 @@ SetLink exts = ['sql', 'gz', 'zip'] ext = tmp[len(tmp) - 1] if ext not in exts: - return public.returnMsg(False, 'DATABASE_INPUT_ERR_FORMAT') + return public.return_msg_gettext(False, 'Select sql/gz/zip file!') + db_find = public.M('databases').where('name=?',name).find() + mysql_obj = public.get_mysql_obj_by_sid(db_find['sid']) + result =mysql_obj.execute("show databases") + isError=self.IsSqlError(result) + if isError: return isError isgzip = False + mysql_bin = public.get_mysql_bin() if ext != 'sql': import panel_restore tmp = file.split('/') @@ -630,69 +928,102 @@ SetLink tmpFile = tmpFile.replace('tar.', '') # return tmpFile backupPath = session['config']['backup_path'] + '/database' - panel_restore.panel_restore().restore_db_backup(get) + if "|" in file: + panel_restore.panel_restore().restore_db_backup(get) + download_msg = panel_restore.panel_restore().restore_db_backup(get) + if not download_msg['status']: + return download_msg + input_path = os.path.join(backupPath, tmpFile) + # 备份文件的路径 + input_path2 = os.path.join(os.path.dirname(file), tmpFile) if ext == 'zip': public.ExecShell("cd " + backupPath + " && unzip " + '"'+file+'"') else: public.ExecShell("cd " + backupPath + " && tar zxf " + '"'+file+'"') - if not os.path.exists(backupPath + "/" + tmpFile): - 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,)) + if not os.path.exists(input_path): + # 兼容从备份文件所在目录恢复 + if not os.path.exists(input_path2): + public.ExecShell("cd " + backupPath + " && gunzip -q " + '"'+file+'"') + isgzip = True + else: + input_path = input_path2 + if not os.path.exists(input_path) or tmpFile == '': + if tmpFile and os.path.isfile(input_path2): + input_path = input_path2 + else: + return public.return_msg_gettext(False, 'Configuration file not exist',(tmpFile,)) try: - password = public.M('config').where('id=?',(1,)).getField('mysql_root') - os.environ["MYSQL_PWD"] = password - public.ExecShell(public.GetConfigValue('setup_path') + "/mysql/bin/mysql -uroot -p" + root + " --force \"" + name + "\" < " +'"'+ backupPath + '/' +tmpFile+'"') + if db_find['db_type'] in ['0',0]: + password = public.M('config').where('id=?',(1,)).getField('mysql_root') + os.environ["MYSQL_PWD"] = str(password) + public.ExecShell(mysql_bin + " -uroot -p" + str(password) + " --force \"" + name + "\" < " +'"'+ input_path +'"') + elif db_find['db_type'] in ['1',1]: + conn_config = json.loads(db_find['conn_config']) + os.environ["MYSQL_PWD"] = str(conn_config['db_password']) + public.ExecShell(mysql_bin + " -h "+ conn_config['db_host'] +" -P "+str(int(conn_config['db_port']))+" -u"+str(conn_config['db_user'])+" -p" + str(conn_config['db_password']) + " --force \"" + name + "\" < " +'"'+ input_path +'"') + elif db_find['db_type'] in ['2',2]: + conn_config = public.M('database_servers').where('id=?',db_find['sid']).find() + os.environ["MYSQL_PWD"] = str(conn_config['db_password']) + public.ExecShell(mysql_bin + " -h "+ conn_config['db_host'] +" -P "+str(int(conn_config['db_port']))+" -u"+str(conn_config['db_user'])+" -p" + str(conn_config['db_password']) + " --force \"" + name + "\" < " +'"'+ input_path +'"') except Exception as e: raise finally: os.environ["MYSQL_PWD"] = "" - - if isgzip: - public.ExecShell('cd ' +backupPath+ ' && gzip ' + file.split('/')[-1][:-3]) + public.ExecShell('cd ' +os.path.dirname(input_path)+ ' && gzip ' + file.split('/')[-1][:-3]) else: - public.ExecShell("rm -f " + backupPath + '/' +tmpFile) + public.ExecShell("rm -f " + input_path) else: try: - password = public.M('config').where('id=?',(1,)).getField('mysql_root') - os.environ["MYSQL_PWD"] = password - public.ExecShell(public.GetConfigValue('setup_path') + "/mysql/bin/mysql -uroot -p" + root + " --force \"" + name + "\" < "+'"' + file+'"') + if db_find['db_type'] in ['0',0]: + password = public.M('config').where('id=?',(1,)).getField('mysql_root') + os.environ["MYSQL_PWD"] = str(password) + public.ExecShell(mysql_bin + " -uroot -p" + root + " --force \"" + name + "\" < " +'"'+ file +'"') + elif db_find['db_type'] in ['1',1]: + conn_config = json.loads(db_find['conn_config']) + os.environ["MYSQL_PWD"] = str(conn_config['db_password']) + public.ExecShell(mysql_bin + " -h "+ conn_config['db_host'] +" -P "+str(int(conn_config['db_port']))+" -u"+str(conn_config['db_user'])+" -p" + str(conn_config['db_password']) + " --force \"" + name + "\" < " +'"'+ file +'"') + elif db_find['db_type'] in ['2',2]: + conn_config = public.M('database_servers').where('id=?',db_find['sid']).find() + os.environ["MYSQL_PWD"] = str(conn_config['db_password']) + public.ExecShell(mysql_bin + " -h "+ conn_config['db_host'] +" -P "+str(int(conn_config['db_port']))+" -u"+str(conn_config['db_user'])+" -p" + str(conn_config['db_password']) + " --force \"" + name + "\" < " +'"'+ file +'"') except Exception as e: raise finally: os.environ["MYSQL_PWD"] = "" - public.WriteLog("TYPE_DATABASE", 'DATABASE_INPUT_SUCCESS',(name,)) - return public.returnMsg(True, 'DATABASE_INPUT_SUCCESS') + public.write_log_gettext("TYPE_DATABASE", 'Successfully imported database [{}]',(name,)) + return public.return_msg_gettext(True, 'Successfully imported database!') #except Exception as ex: #public.WriteLog("TYPE_DATABASE", 'DATABASE_INPUT_ERR',(name,str(ex))) #return public.returnMsg(False,'DATABASE_INPUT_ERR') #同步数据库到服务器 def SyncToDatabases(self,get): - result = panelMysql.panelMysql().execute("show databases") - isError=self.IsSqlError(result) - if isError: return isError + # result = panelMysql.panelMysql().execute("show databases") + # isError=self.IsSqlError(result) + # if isError: return isError type = int(get['type']) n = 0 sql = public.M('databases') if type == 0: - data = sql.field('id,name,username,password,accept').select() + data = sql.field('id,sid,name,username,password,accept,db_type').select() for value in data: + if value['db_type'] in ['1',1]: + continue # 跳过远程数据库 result = self.ToDataBase(value) if result == 1: n +=1 else: import json data = json.loads(get.ids) for value in data: - find = sql.where("id=?",(value,)).field('id,name,username,password,accept').find() + find = sql.where("id=?",(value,)).field('id,sid,name,username,password,accept').find() result = self.ToDataBase(find) if result == 1: n +=1 - return public.returnMsg(True,'DATABASE_SYNC_SUCCESS',(str(n),)) + return public.return_msg_gettext(True,'Sync {} database(s) from server!',(str(n),)) #配置 def mypass(self,act,password = None): @@ -727,8 +1058,9 @@ SetLink find['username'] = find['name'] find['password'] = public.md5(str(time.time()) + find['name'])[0:10] public.M('databases').where("id=?",(find['id'],)).save('password,username',(find['password'],find['username'])) - - result = panelMysql.panelMysql().execute("create database `" + find['name'] + "`") + self.sid = find['sid'] + mysql_obj = public.get_mysql_obj_by_sid(find['sid']) + result = mysql_obj.execute("create database `" + find['name'] + "`") if "using password:" in str(result): return -1 if "Connection refused" in str(result): return -1 @@ -742,11 +1074,17 @@ SetLink #从服务器获取数据库 def SyncGetDatabases(self,get): - data = panelMysql.panelMysql().query("show databases") + self.sid = get.get('sid/d',0) + db_type = 0 + if self.sid: db_type = 2 + mysql_obj = public.get_mysql_obj_by_sid(self.sid) + data = mysql_obj.query("show databases") isError = self.IsSqlError(data) if isError != None: return isError - users = panelMysql.panelMysql().query("select User,Host from mysql.user where User!='root' AND Host!='localhost' AND Host!=''") + users = mysql_obj.query("select User,Host from mysql.user where User!='root' AND Host!='localhost' AND Host!=''") + if type(users) == str: return public.returnMsg(False,users) + if type(users) != list: return public.returnMsg(False,public.GetMySQLError(users)) sql = public.M('databases') nameArr = ['information_schema','performance_schema','mysql','sys'] @@ -765,37 +1103,43 @@ SetLink host = user[1] break - ps = public.getMsg('INPUT_PS') + ps = public.get_msg_gettext('Edit notes') if value[0] == 'test': - ps = public.getMsg('DATABASE_TEST') + ps = public.get_msg_gettext('Test Database') # XSS filter if not re.match("^[\w+\.-]+$",value[0]):continue addTime = time.strftime('%Y-%m-%d %X',time.localtime()) - if sql.table('databases').add('name,username,password,accept,ps,addtime',(value[0],value[0],'',host,ps,addTime)): n +=1 + + if sql.table('databases').add('name,sid,db_type,username,password,accept,ps,addtime',(value[0],self.sid,db_type,value[0],'',host,ps,addTime)): n +=1 - return public.returnMsg(True,'DATABASE_GET_SUCCESS',(str(n),)) + return public.return_msg_gettext(True,'Obtain {} database(s) from server!',(str(n),)) #获取数据库权限 def GetDatabaseAccess(self,get): name = get['name'] - users = panelMysql.panelMysql().query("select Host,ssl_type from mysql.user where User='" + name + "' AND Host!='localhost'") + db_name = public.M('databases').where('username=?',name).getField('name') + mysql_obj = public.get_mysql_obj(db_name) + users = mysql_obj.query("select Host from mysql.user where User='" + name + "' AND Host!='localhost'") ssl_type = panelMysql.panelMysql().query("select ssl_type from mysql.user where User='%s'" % name) isError = self.IsSqlError(users) if isError != None: return isError users = self.map_to_list(users) - if ssl_type: - ssl_type = ssl_type[0][0] + try: + if ssl_type: + ssl_type = ssl_type[0][0] + except: + ssl_type = "" if len(users)<1: - return public.returnMsg(True,{"permission":"127.0.0.1","ssl":ssl_type}) + return public.return_msg_gettext(True,{"permission":"127.0.0.1","ssl":ssl_type}) accs = [] for c in users: accs.append(c[0]) userStr = ','.join(accs) - return public.returnMsg(True,{"permission":userStr,"ssl":ssl_type}) + return public.return_msg_gettext(True,{"permission":userStr,"ssl":ssl_type}) #设置数据库权限 def SetDatabaseAccess(self,get): @@ -803,13 +1147,15 @@ SetLink if hasattr(get,'ssl'): ssl = get.ssl if ssl == "REQUIRE SSL" and not self.check_mysql_ssl_status(get): - return public.returnMsg(False,'SSL is not enabled in the database, please open it in the Mysql manager first') + return public.return_msg_gettext(False,'SSL is not enabled in the database, please open it in the Mysql manager first') name = get['name'] - db_name = public.M('databases').where('username=?',(name,)).getField('name') + db_find = public.M('databases').where('username=?',(name,)).find() + db_name = db_find['name'] + self.sid = db_find['sid'] + mysql_obj = public.get_mysql_obj(db_name) access = get['access'].strip() - if access in ['']: return public.returnMsg(False,'The IP address cannot be empty!') + if access in ['']: return public.return_msg_gettext(False,'The IP address cannot be empty!') password = public.M('databases').where("username=?",(name,)).getField('password') - mysql_obj = panelMysql.panelMysql() result = mysql_obj.query("show databases") isError = self.IsSqlError(result) if isError != None: return isError @@ -817,7 +1163,7 @@ SetLink for us in users: mysql_obj.execute("drop user '" + name + "'@'" + us[0] + "'") self.__CreateUsers(db_name,name,password,access,ssl) - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') #获取数据库配置信息 def GetMySQLInfo(self,get): @@ -838,11 +1184,11 @@ SetLink #修改数据库目录 def SetDataDir(self,get): if get.datadir[-1] == '/': get.datadir = get.datadir[0:-1] - if len(get.datadir) > 32: return public.returnMsg(False,'The data directory length cannot exceed 32 bits') - if not re.search(r"^[0-9A-Za-z_/\\]$+",get.datadir): return public.returnMsg(False,'Special symbols cannot be included in the database path') + if len(get.datadir) > 32: return public.return_msg_gettext(False,'The data directory length cannot exceed 32 bits') + # if not re.search(r"^[0-9A-Za-z_/\\]$+",get.datadir): return public.return_msg_gettext(False,'Special symbols cannot be included in the database path') if not os.path.exists(get.datadir): public.ExecShell('mkdir -p ' + get.datadir) mysqlInfo = self.GetMySQLInfo(get) - if mysqlInfo['datadir'] == get.datadir: return public.returnMsg(False,'DATABASE_MOVE_RE') + if mysqlInfo['datadir'] == get.datadir: return public.return_msg_gettext(False,'The same as the current storage directory, file cannot be moved!') public.ExecShell('/etc/init.d/mysqld stop') public.ExecShell('\cp -arf ' + mysqlInfo['datadir'] + '/* ' + get.datadir + '/') @@ -861,12 +1207,12 @@ SetLink result = public.ExecShell('ps aux|grep mysqld|grep -v grep') if len(result[0]) > 10: public.writeFile('data/datadir.pl',get.datadir) - return public.returnMsg(True,'DATABASE_MOVE_SUCCESS') + return public.return_msg_gettext(True,'Database moved!') else: public.ExecShell('pkill -9 mysqld') public.writeFile(myfile,public.readFile('/etc/my_backup.cnf')) public.ExecShell('/etc/init.d/mysqld start') - return public.returnMsg(False,'DATABASE_MOVE_ERR') + return public.return_msg_gettext(False,'Failed to move file!') #修改数据库端口 def SetMySQLPort(self,get): @@ -876,7 +1222,7 @@ SetLink mycnf = re.sub(rep,'port = ' + get.port + '\n',mycnf) public.writeFile(myfile,mycnf) public.ExecShell('/etc/init.d/mysqld restart') - return public.returnMsg(True,'EDIT_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #获取错误日志 def GetErrorLog(self,get): @@ -887,10 +1233,10 @@ SetLink if n[-3:] == 'err': filename = path + '/' + n break - if not os.path.exists(filename): return public.returnMsg(False,'FILE_NOT_EXISTS') + if not os.path.exists(filename): return public.return_msg_gettext(False,'Configuration file not exist') if hasattr(get,'close'): public.writeFile(filename,'') - return public.returnMsg(True,'LOG_CLOSE') + return public.return_msg_gettext(True,'log is empty') return public.GetNumLines(filename,1000) #二进制日志开关 @@ -899,23 +1245,23 @@ SetLink mycnf = public.readFile(myfile) masterslaveconf = "/www/server/panel/plugin/masterslave/data.json" if mycnf.find('#log-bin=mysql-bin') != -1: - if hasattr(get,'status'): return public.returnMsg(False,'0') + if hasattr(get,'status'): return public.return_msg_gettext(False,'0') mycnf = mycnf.replace('#log-bin=mysql-bin','log-bin=mysql-bin') mycnf = mycnf.replace('#binlog_format=mixed','binlog_format=mixed') public.ExecShell('sync') public.ExecShell('/etc/init.d/mysqld restart') else: path = self.GetMySQLInfo(get)['datadir'] - if not os.path.exists(path): return public.returnMsg(False,'MYSQL_DATA_DIR_ERR') + if not os.path.exists(path): return public.return_msg_gettext(False,'The database directory does not exist!') if hasattr(get,'status'): dsize = 0 for n in os.listdir(path): if len(n) < 9: continue if n[0:9] == 'mysql-bin': dsize += os.path.getsize(path + '/' + n) - return public.returnMsg(True,dsize) + return public.return_msg_gettext(True,dsize) if os.path.exists(masterslaveconf): - return public.returnMsg(False, "MYSQL_BINLOG_ERR") + return public.return_msg_gettext(False, 'Please uninstall the Mysql master-slave replication plugin before closing the binary log! !') mycnf = mycnf.replace('log-bin=mysql-bin','#log-bin=mysql-bin') mycnf = mycnf.replace('binlog_format=mixed','#binlog_format=mixed') public.ExecShell('sync') @@ -923,7 +1269,7 @@ SetLink public.ExecShell('rm -f ' + path + '/mysql-bin.*') public.writeFile(myfile,mycnf) - return public.returnMsg(True,'SUCCESS') + return public.return_msg_gettext(True,'Login succeeded, loading...') #获取MySQL配置状态 def GetDbStatus(self,get): @@ -960,7 +1306,7 @@ SetLink if g in ['innodb_log_buffer_size']: s = 'M' if int(get[g]) < 8: - return public.returnMsg(False,'MYSQL_PARAMETER_ERR') + return public.return_msg_gettext(False,'innodb_log_buffer_size cannot be less than 8MB') rep = r'\s*'+g+r'\s*=\s*\d+(M|K|k|m|G)?\n' @@ -971,7 +1317,7 @@ SetLink mycnf = mycnf.replace('[mysqld]\n','[mysqld]\n' +c) n+=1 public.writeFile('/etc/my.cnf',mycnf) - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #获取MySQL运行状态 def GetRunStatus(self,get): @@ -981,7 +1327,7 @@ SetLink gets = ['Max_used_connections','Com_commit','Com_rollback','Questions','Innodb_buffer_pool_reads','Innodb_buffer_pool_read_requests','Key_reads','Key_read_requests','Key_writes','Key_write_requests','Qcache_hits','Qcache_inserts','Bytes_received','Bytes_sent','Aborted_clients','Aborted_connects','Created_tmp_disk_tables','Created_tmp_tables','Innodb_buffer_pool_pages_dirty','Opened_files','Open_tables','Opened_tables','Select_full_join','Select_range_check','Sort_merge_passes','Table_locks_waited','Threads_cached','Threads_connected','Threads_created','Threads_running','Connections','Uptime'] try: if data[0] == 1045: - return public.returnMsg(False,'MYSQL_PASS_ERR') + return public.return_msg_gettext(False,'MySQL password ERROR!') for d in data: for g in gets: try: @@ -989,7 +1335,7 @@ SetLink except: pass except: - return public.returnMsg(False,str(data)) + return public.return_msg_gettext(False,str(data)) if not 'Run' in result and result: result['Run'] = int(time.time()) - int(result['Uptime']) @@ -1007,8 +1353,8 @@ SetLink #取慢日志 def GetSlowLogs(self,get): path = self.GetMySQLInfo(get)['datadir'] + '/mysql-slow.log' - if not os.path.exists(path): return public.returnMsg(False,'AJAX_LOG_FILR_NOT_EXISTS') - return public.returnMsg(True,public.GetNumLines(path,100)) + if not os.path.exists(path): return public.return_msg_gettext(False,'Log file does NOT exist!') + return public.return_msg_gettext(True,public.GetNumLines(path,100)) # 获取当前数据库信息 @@ -1018,33 +1364,31 @@ SetLink if info: return info else: - return public.returnMsg(False,"GET_DB_ERR") + return public.return_msg_gettext(False,'Failed to get databases') #修复表信息 def ReTable(self,get): - m_version = public.readFile(public.GetConfigValue('setup_path') + '/mysql/version.pl') - if m_version.find('5.1.')!=-1:return public.returnMsg(False,"NONSUPPORT51") info=self.RepairTable(get) if info: - return public.returnMsg(True,"REPAIR_SUCCESS") + return public.return_msg_gettext(True,'Successfully repaired!') else: - return public.returnMsg(False,"REPAIR_FAILURE") + return public.return_msg_gettext(False,'Failed to repair!') # 优化表 def OpTable(self,get): info=self.OptimizeTable(get) if info: - return public.returnMsg(True,"OPTIMIZED_SUCCESS") + return public.return_msg_gettext(True,'Successfully optimized!') else: - return public.returnMsg(False,"OPTIMIZED_FAIL_OR_ALREADY_OPTIMIZED") + return public.return_msg_gettext(False,'Failed to optimize or already optimized') #更改表引擎 def AlTable(self,get): info=self.AlterTable(get) if info: - return public.returnMsg(True,"CHANGE_SUCCESS") + return public.return_msg_gettext(True,'Successfully changed') else: - return public.returnMsg(False,"CHANVE_FAIL") + return public.return_msg_gettext(False,'Failed to change') def get_average_num(self,slist): """ @@ -1059,21 +1403,20 @@ SetLink limit_size =int((slist[0] + slist[-1])/2 * 0.85) return limit_size - def get_database_size(self,get): + def get_database_size(self,ids,is_pid = False): """ 获取数据库大小 """ result = {} - tables = public.get_database_size() - data = public.M('databases').field('id,pid,name,ps,addtime').select() - public.print_log(data) - for x in data: - name = x['name'] - x['total'] = 0 + for id in ids: + if not is_pid: + x = public.M('databases').where('id=?',id).field('id,sid,pid,name,ps,addtime').find() + else: + x = public.M('databases').where('pid=?',id).field('id,sid,pid,name,ps,addtime').find() + if not x: continue x['backup_count'] = public.M('backup').where("pid=? AND type=?",(x['id'],'1')).count() - if name in tables: x['total'] = tables[name] - - result[name] = x + x['total'] = int(public.get_database_size_by_id(x['id'])) + result[x['name']] = x return result def check_del_data(self,get): @@ -1082,7 +1425,7 @@ SetLink """ ids = json.loads(get.ids) slist = {};result = [];db_list_size = [] - db_data = self.get_database_size(None) + db_data = self.get_database_size(ids) for key in db_data: data = db_data[key] if not data['id'] in ids: continue diff --git a/class/datatool.py b/class/datatool.py index 50d8d40f..c289f647 100644 --- a/class/datatool.py +++ b/class/datatool.py @@ -25,17 +25,17 @@ class datatools: for d in ds: if size < 1024: return ('%.2f' % size) + d size = size / 1024 - return '0b'; + return '0b' # 获取当前数据库信息 def GetdataInfo(self,get): ''' 传递一个数据库名称即可 get.databases ''' - if not self.DB_MySQL:self.DB_MySQL = panelMysql.panelMysql() - db_name=get.db_name + db_name=get.db_name if not db_name:return False + if not self.DB_MySQL:self.DB_MySQL = public.get_mysql_obj(db_name) ret = {} tables = self.map_to_list(self.DB_MySQL.query('show tables from `%s`' % db_name)) if type(tables) == list: @@ -50,7 +50,7 @@ class datatools: ret3 = [] for i in tables: - if i == 1049: return public.returnMsg(False,'DB_NOT_EXIST') + if i == 1049: return public.return_msg_gettext(False,'Database does NOT exist!') if type(i) == int: continue table = self.map_to_list(self.DB_MySQL.query("show table status from `%s` where name = '%s'" % (db_name, i[0]))) if not table: continue @@ -80,7 +80,9 @@ class datatools: db_name = get.db_name tables = json.loads(get.tables) if not db_name or not tables: return False - if not self.DB_MySQL:self.DB_MySQL = panelMysql.panelMysql() + if not self.DB_MySQL:self.DB_MySQL = public.get_mysql_obj(db_name) + m_version = self.DB_MySQL.query('select version();')[0][0] + if m_version.find('5.1.')!=-1:return public.return_msg_gettext(False,"Nonsupport mysql5.1!") mysql_table = self.map_to_list(self.DB_MySQL.query('show tables from `%s`' % db_name)) ret=[] if type(mysql_table)==list: @@ -112,10 +114,11 @@ class datatools: db_name=web tables=['web1','web2'] ''' - if not self.DB_MySQL:self.DB_MySQL = panelMysql.panelMysql() + db_name = get.db_name tables = json.loads(get.tables) if not db_name or not tables: return False + if not self.DB_MySQL:self.DB_MySQL = public.get_mysql_obj(db_name) mysql_table = self.map_to_list(self.DB_MySQL.query('show tables from `%s`' % db_name)) ret=[] if type(mysql_table) == list: @@ -138,13 +141,12 @@ class datatools: table_type=innodb tables=['web1','web2'] ''' - if not self.DB_MySQL:self.DB_MySQL = panelMysql.panelMysql() db_name = get.db_name table_type = get.table_type tables = json.loads(get.tables) if not db_name or not tables: return False - + if not self.DB_MySQL:self.DB_MySQL = public.get_mysql_obj(db_name) mysql_table = self.map_to_list(self.DB_MySQL.query('show tables from `%s`' % db_name)) ret=[] if type(mysql_table)==list: diff --git a/class/db.py b/class/db.py index f47b6f9c..5a5aa214 100644 --- a/class/db.py +++ b/class/db.py @@ -304,23 +304,26 @@ class Sql(): #是否有锁 def is_lock(self): - n = 0 - while os.path.exists(self.__LOCK): - n+=1 - if n > 100: - self.rm_lock() - break - time.sleep(0.01) + return + # n = 0 + # while os.path.exists(self.__LOCK): + # n+=1 + # if n > 100: + # self.rm_lock() + # break + # time.sleep(0.01) #写锁 def write_lock(self): - self.is_lock() - with open(self.__LOCK,'wb+') as f: - f.close() + return + # self.is_lock() + # with open(self.__LOCK,'wb+') as f: + # f.close() #解锁 def rm_lock(self): - if os.path.exists(self.__LOCK): - os.remove(self.__LOCK) + return + # if os.path.exists(self.__LOCK): + # os.remove(self.__LOCK) def query(self,sql,param = ()): #执行SQL语句返回数据集 diff --git a/class/db_mysql.py b/class/db_mysql.py index 60ec895f..d9aa07a6 100644 --- a/class/db_mysql.py +++ b/class/db_mysql.py @@ -6,73 +6,74 @@ # +------------------------------------------------------------------- # | Author: hwliang # +------------------------------------------------------------------- -import re, os, sys, public, json + +import re,os,sys,public,json import pymysql - -class mysql: - __DB_PASS = '' - __DB_USER = '' - __DB_NAME = '' +class panelMysql: + __DB_PASS = None + __DB_USER = 'root' + __DB_NAME = None __DB_PORT = 3306 __DB_HOST = 'localhost' __DB_PREFIX = '' __DB_CONN = None - __DB_CUR = None - __DB_ERR = None - __DB_NET = None - __DB_TABLE = "" # 被操作的表名称 - __OPT_WHERE = "" # where条件 - __OPT_LIMIT = "" # limit条件 - __OPT_ORDER = "" # order条件 - __OPT_FIELD = "*" # field条件 - __OPT_PARAM = () # where值 + __DB_CUR = None + __DB_ERR = None + __DB_TABLE = "" # 被操作的表名称 + __OPT_WHERE = "" # where条件 + __OPT_LIMIT = "" # limit条件 + __OPT_ORDER = "" # order条件 + __OPT_FIELD = "*" # field条件 + __OPT_PARAM = () # where值 + _USER = None def __init__(self): pass - def set_name(self, name): - self.__DB_NAME = name + def set_name(self,name): + self.__DB_NAME = str(name) return self - def set_host(self, host, port, name, username, password, prefix=''): - self.__DB_HOST = host - self.__DB_PORT = port - self.__DB_NAME = name - self.__DB_USER = username - self.__DB_PASS = password + def set_prefix(self,prefix): self.__DB_PREFIX = prefix return self + def set_host(self,host,port,name,username,password,prefix = ''): + self.__DB_HOST = host + self.__DB_PORT = int(port) + self.__DB_NAME = name + if self.__DB_NAME: self.__DB_NAME = str(self.__DB_NAME) + self.__DB_USER = str(username) + self._USER = str(username) + self.__DB_PASS = str(password) + self.__DB_PREFIX = prefix + self.__GetConn() + return self + #连接MYSQL数据库 def __GetConn(self): - if self.__DB_NET: return True try: - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, - port=self.__DB_PORT, - user=self.__DB_USER, - passwd=self.__DB_PASS) + self.__DB_CONN = pymysql.connect(host=self.__DB_HOST,user=self.__DB_USER,passwd=str(self.__DB_PASS),db=self.__DB_NAME,port=self.__DB_PORT,connect_timeout=15,read_timeout=60,write_timeout=60) + except: + self.__DB_CONN = pymysql.connect(host=self.__DB_HOST,user=self.__DB_USER,passwd=str(self.__DB_PASS),db=self.__DB_NAME,port=self.__DB_PORT) + self.__DB_CUR = self.__DB_CONN.cursor() + return True - self.__DB_CUR = self.__DB_CONN.cursor() - self.__DB_NET = True - return True - except pymysql.Error as e: - self.__DB_ERR = e - return False - - def table(self, table): + def table(self,table): #设置表名 self.__DB_TABLE = self.__DB_PREFIX + table return self - def where(self, where, param): + + def where(self,where,param): #WHERE条件 if where: self.__OPT_WHERE = " WHERE " + where self.__OPT_PARAM = self.__to_tuple(param) return self - def __to_tuple(self, param): + def __to_tuple(self,param): #将参数转换为tuple if type(param) != tuple: if type(param) == list: @@ -103,6 +104,7 @@ class mysql: def select(self): #查询数据集 self.__GetConn() + if not self.__DB_CUR: return self.__DB_ERR try: self.__get_columns() sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + self.__OPT_WHERE + self.__OPT_ORDER + self.__OPT_LIMIT @@ -119,18 +121,18 @@ class mysql: tmp1[key.strip('`')] = row[i] i += 1 tmp.append(tmp1) - del (tmp1) + del(tmp1) data = tmp - del (tmp) + del(tmp) else: #将元组转换成列表 tmp = list(map(list, data)) data = tmp - del (tmp) + del(tmp) self.__close() return data except Exception as ex: - return public.get_error_info() + return "error: " + str(ex) def get(self): self.__get_columns() @@ -291,30 +293,34 @@ class mysql: except Exception as ex: return "error: " + str(ex) - def execute(self, sql, is_close=True): + def execute(self,sql,param = ()): #执行SQL语句返回受影响行 if not self.__GetConn(): return self.__DB_ERR try: - result = self.__DB_CUR.execute(sql) + self.__OPT_PARAM = list(self.__to_tuple(param)) + result = self.__DB_CUR.execute(sql,self.__OPT_PARAM) self.__DB_CONN.commit() - if is_close: self.__close() + self.__close() return result except Exception as ex: return ex - def query(self, sql, is_close=True): + + def query(self,sql,is_close=True,param=()): #执行SQL语句返回数据集 if not self.__GetConn(): return self.__DB_ERR try: - self.__DB_CUR.execute(sql) + self.__OPT_PARAM = list(self.__to_tuple(param)) + self.__DB_CUR.execute(sql,self.__OPT_PARAM) result = self.__DB_CUR.fetchall() #将元组转换成列表 - data = list(map(list, result)) + data = list(map(list,result)) if is_close: self.__Close() return data except Exception as ex: return ex + #关闭连接 def __Close(self): self.__DB_CUR.close() diff --git a/class/file_execute_deny.py b/class/file_execute_deny.py index 89623026..606ebee9 100644 --- a/class/file_execute_deny.py +++ b/class/file_execute_deny.py @@ -51,7 +51,7 @@ class FileExecuteDeny: deny_name.append(tmp[-1]) result = [] for i in deny_name: - reg = '#BEGIN_DENY_{}\n\s*location\s*\~\*\s*\^(.*)\.\*.*\((.*)\)\$'.format(i) + reg = '#BEGIN_DENY_{}\n\s*location\s*\~\*\s*\^(.*)\.\*.*\((.*)\)\$'.format(i.replace("|","\|")) deny_directory = re.search(reg,conf).groups()[0] deny_suffix = re.search(reg,conf).groups()[1] result.append({'name':i,'dir':deny_directory,'suffix':deny_suffix}) @@ -71,7 +71,7 @@ class FileExecuteDeny: deny_name.append(tmp[-1]) result = [] for i in deny_name: - reg = '#BEGIN_DENY_{}\n\s* # +------------------------------------------------------------------- +from base64 import b64encode import sys import os import public @@ -28,38 +29,41 @@ class files: sqlite_connection = None download_list = None download_is_rm = None - #检查敏感目录 - def CheckDir(self,path): - path = path.replace('//','/') + recycle_list = [] + # 检查敏感目录 + + def CheckDir(self, path): + path = path.replace('//', '/') if path[-1:] == '/': path = path[:-1] - + nDirs = ('', '/', - '/*', - '/www', - '/root', - '/boot', - '/bin', - '/etc', - '/home', - '/dev', - '/sbin', - '/var', - '/usr', - '/tmp', - '/sys', - '/proc', - '/media', - '/mnt', - '/opt', - '/lib', - '/srv', - '/selinux', - '/www/server', - '/www/server/data', - public.GetConfigValue('logs_path'), - public.GetConfigValue('setup_path')) + '/*', + '/www', + '/root', + '/boot', + '/bin', + '/etc', + '/home', + '/dev', + '/sbin', + '/var', + '/usr', + '/tmp', + '/sys', + '/proc', + '/media', + '/mnt', + '/opt', + '/lib', + '/srv', + '/selinux', + '/www/server', + '/www/server/data', + '/www/.Recycle_bin', + public.GetConfigValue('logs_path'), + public.GetConfigValue('setup_path')) return not path in nDirs @@ -116,7 +120,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) siteName = find['name'] sitePath = find['path'] if public.get_webserver() == 'nginx': - filename = '/www/server/panel/vhost/nginx/' + siteName + '.conf' + filename = public.get_vhost_path() + '/nginx/' + siteName + '.conf' if os.path.exists(filename): conf = public.readFile(filename) rep = '\s*root\s+(.+);' @@ -124,7 +128,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) if tmp1: path = tmp1.groups()[0] else: - filename = '/www/server/panel/vhost/apache/' + siteName + '.conf' + filename = public.get_vhost_path() + '/apache/' + siteName + '.conf' if os.path.exists(filename): conf = public.readFile(filename) rep = '\s*DocumentRoot\s*"(.+)"\s*\n' @@ -218,6 +222,37 @@ session.save_handler = files'''.format(path, sess_path, sess_path) return False return True + # 上传前检查文件是否存在 + def upload_file_exists(self,args): + ''' + @name 上传前检查文件是否存在 + @author hwliang<2021-11-3> + @param filename 文件名 + @return dict + ''' + filename = args.filename.strip() + if not os.path.exists(filename): + return public.returnMsg(False,'File does not exist!') + file_info = {} + _stat = os.stat(filename) + file_info['size'] = _stat.st_size + file_info['mtime'] = int(_stat.st_mtime) + file_info['isfile'] = os.path.isfile(filename) + return public.returnMsg(True,file_info) + + + def get_real_len(self,string): + ''' + @name 获取含中文的字符串字精确长度 + @author hwliang<2021-11-3> + @param string + @return int + ''' + real_len = len(string) + for s in string: + if '\u2E80' <= s <= '\uFE4F': + real_len += 1 + return real_len # 上传文件2 def upload(self, args): @@ -230,15 +265,17 @@ session.save_handler = files'''.format(path, sess_path, sess_path) 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 self.f_name_check(args.f_name): return public.returnMsg(False,'FILE_NAME_ERR') + try: + if self.get_real_len(args.f_name) > 128: return public.return_msg_gettext(False,'The file name contains more than 128 bytes') + except: + pass + if not self.f_name_check(args.f_name): return public.return_msg_gettext(False,'No special characters can be included in the file name!') if args.f_path == '/': - return public.returnMsg(False,'UPLOAD_DIR_ERR') + return public.return_msg_gettext(False,'Cannot upload files to the system document root!') if args.f_name.find('./') != -1 or args.f_path.find('./') != -1: - return public.returnMsg(False, 'WRONG_PARAMETER') + return public.return_msg_gettext(False, 'Wrong parameter') if not os.path.exists(args.f_path): os.makedirs(args.f_path, 493) if not 'dir_mode' in args or not 'file_mode' in args: @@ -251,16 +288,21 @@ session.save_handler = files'''.format(path, sess_path, sess_path) d_size = os.path.getsize(save_path) if d_size != int(args.f_start): return d_size - f = open(save_path, 'ab') - if 'b64_data' in args: - import base64 - b64_data = base64.b64decode(args.b64_data) - f.write(b64_data) - else: - upload_files = request.files.getlist("blob") - for tmp_f in upload_files: - f.write(tmp_f.read()) - f.close() + try: + f = open(save_path, 'ab') + if 'b64_data' in args: + import base64 + b64_data = base64.b64decode(args.b64_data) + f.write(b64_data) + else: + upload_files = request.files.getlist("blob") + for tmp_f in upload_files: + f.write(tmp_f.read()) + f.close() + except Exception as ex: + ex = str(ex) + if ex.find('No space left on device') != -1: + return public.returnMsg(False, 'Not enough disk space') f_size = os.path.getsize(save_path) if f_size != int(args.f_size): return f_size @@ -286,12 +328,13 @@ session.save_handler = files'''.format(path, sess_path, sess_path) if new_name.find('.user.ini') != -1: public.ExecShell("chattr +i " + new_name) - public.WriteLog('TYPE_FILE', 'FILE_UPLOAD_SUCCESS', + public.write_log_gettext('File manager', 'Successfully uploaded!', (args.f_name, args.f_path)) - return public.returnMsg(True, 'FILE_UPLOAD_SUCCESS') + return public.return_msg_gettext(True, 'Successfully uploaded!') # 设置文件和目录权限 def set_mode(self, path): + if path[-1] == '/': path = path[:-1] s_path = os.path.dirname(path) p_stat = os.stat(s_path) os.chown(path,p_stat.st_uid,p_stat.st_gid) @@ -330,7 +373,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) def GetDir(self, get): if not hasattr(get, 'path'): # return public.returnMsg(False,'错误的参数!') - get.path = '/www/wwwroot' + get.path = public.get_site_path() #'/www/wwwroot' if sys.version_info[0] == 2: get.path = get.path.encode('utf-8') if get.path == '': @@ -342,15 +385,15 @@ session.save_handler = files'''.format(path, sess_path, sess_path) get.path = self.xssdecode(get.path) if not os.path.exists(get.path): - get.path = '/www/wwwroot' + get.path = public.get_site_path() #return public.ReturnMsg(False, '指定目录不存在!') - if get.path == '/www/Recycle_bin': - return public.returnMsg(False,'RECYCLE_BIN_ERR') + if os.path.basename(get.path) == '.Recycle_bin': + return public.return_msg_gettext(False,'Recovery failed!') if not os.path.isdir(get.path): get.path = os.path.dirname(get.path) if not os.path.isdir(get.path): - return public.returnMsg(False,'DIR_ERR') + return public.return_msg_gettext(False,'This is not a directory') import pwd dirnames = [] @@ -499,7 +542,8 @@ session.save_handler = files'''.format(path, sess_path, sess_path) @param filename 文件或目录全路径 @return string ''' - ps_path = '/www/server/panel/data/files_ps' + + ps_path = public.get_panel_path() + '/data/files_ps' f_key1 = '/'.join((ps_path,public.md5(filename))) if os.path.exists(f_key1): return public.readFile(f_key1) @@ -528,9 +572,35 @@ session.save_handler = files'''.format(path, sess_path, sess_path) '/www/server/cron':'Cron script and log directory', '/www/server/php':'All interpreters of PHP versions are in this directory', '/www/server/tomcat':'Tomcat program directory', - '/www/php_session':'PHP-SESSION Quarantine directory' + '/www/php_session':'PHP-SESSION Quarantine directory', + '/proc': 'System process directory', + '/dev': 'System Device Catalog', + '/sys': 'System call directory', + '/tmp': 'System temporary file directory', + '/var/log': 'System log directory', + '/var/run': 'System operation log directory', + '/var/spool': 'System queue directory', + '/var/lock': 'System lock directory', + '/var/mail': 'System mail directory', + '/mnt': 'System mount directory', + '/media': 'Multimedia catalog', + '/dev/shm': 'Shared memory directory', + '/lib': 'System dynamic library directory', + '/lib64': 'System dynamic library directory', + '/lib32': 'System dynamic library directory', + '/usr/lib': 'System dynamic library directory', + '/usr/lib64': 'System dynamic library directory', + '/usr/local/lib': 'System dynamic library directory', + '/usr/local/lib64': 'System dynamic library directory', + '/usr/local/libexec': 'System dynamic library directory', + '/usr/local/sbin': 'System script directory', + '/usr/local/bin': 'System script directory' } - if filename in pss: return pss[filename] + if filename in pss: return "PS:" + pss[filename] + + if not self.recycle_list: self.recycle_list = public.get_recycle_bin_list() + if filename + '/' in self.recycle_list: return 'PS: Recycle Bin Directory' + if filename in self.recycle_list: return 'PS: Recycle Bin Directory' return '' @@ -545,8 +615,8 @@ session.save_handler = files'''.format(path, sess_path, sess_path) ''' filename = args.filename.strip() ps_type = int(args.ps_type) - ps_body = public.xssencode(args.ps_body) - ps_path = '/www/server/panel/data/files_ps' + ps_body = public.xssencode2(args.ps_body) + ps_path = public.get_panel_path() + '/data/files_ps' if not os.path.exists(ps_path): os.makedirs(ps_path,384) if ps_type == 1: @@ -558,11 +628,11 @@ session.save_handler = files'''.format(path, sess_path, sess_path) f_key = '/'.join((ps_path,ps_key)) if ps_body: public.writeFile(f_key,ps_body) - public.WriteLog('TYPE_FILE','SET_FILE_NOTES',(f_name,ps_body)) + public.write_log_gettext('File manager','Set the file name [{}], notes: {}',(f_name,ps_body)) else: if os.path.exists(f_key):os.remove(f_key) - public.WriteLog('TYPE_FILE','CLEAR_FILE_NOTES',(f_name)) - return public.returnMsg(True,'SET_SUCCESS') + public.write_log_gettext('File manager','Clear file notes [{}]',(f_name)) + return public.return_msg_gettext(True,'Setup successfully!') @@ -634,7 +704,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) def SearchFiles(self, get): if not hasattr(get, 'path'): - get.path = '/www/wwwroot' + get.path = public.get_site_path() if sys.version_info[0] == 2: get.path = get.path.encode('utf-8') if not os.path.exists(get.path): @@ -738,21 +808,24 @@ session.save_handler = files'''.format(path, sess_path, sess_path) if sys.version_info[0] == 2: get.path = get.path.encode('utf-8').strip() try: + fname = os.path.basename(get.path).strip() + fpath = os.path.dirname(get.path).strip() + get.path = os.path.join(fpath,fname) if get.path[-1] == '.': - return public.returnMsg(False, 'File_END_WITH') + return public.return_msg_gettext(False, 'It is not recommended to use [ . ] at the end of the file because there may be security risks') if not self.CheckFileName(get.path): - return public.returnMsg(False, 'FILE_NAME_SPECIAL_CHARACTRES') + return public.return_msg_gettext(False, 'File names can NOT contain special characters!') if os.path.exists(get.path): - return public.returnMsg(False, 'FILE_EXISTS') + return public.return_msg_gettext(False, 'Requested file exists!') path = os.path.dirname(get.path) if not os.path.exists(path): os.makedirs(path) open(get.path, 'w+').close() self.SetFileAccept(get.path) - public.WriteLog('TYPE_FILE', 'FILE_CREATE_SUCCESS', (get.path,)) - return public.returnMsg(True, 'FILE_CREATE_SUCCESS') + public.write_log_gettext('TYPE_FILE', 'Successfully created file [{}]!', (get.path,)) + return public.return_msg_gettext(True, 'Successfully created file!') except: - return public.returnMsg(False, 'FILE_CREATE_ERR') + return public.return_msg_gettext(False, 'Failed to create file!') #创建软链 def CreateLink(self,get): @@ -765,15 +838,19 @@ session.save_handler = files'''.format(path, sess_path, sess_path) }> @return dict ''' - - if not 'sfile' in get: return public.returnMsg(False,'INIT_ARGS_ERR') - if not os.path.exists(get.sfile): return public.returnMsg(False,'FILE_NOT_EXISTS') - if os.path.exists(get.dfile): return public.returnMsg(False,'SOFTCHAIN_FILE_EXIST') - if get.dfile[0] != '/': return public.returnMsg(False,'SOFTCHAIN_FILE_PATH') + if not get.dfile or get.dfile[-1] == "/": + return public.return_msg_gettext(False, + 'The specified soft link file name must contain the full path (full path)') + if not 'sfile' in get: return public.return_msg_gettext(False,'Parameter ERROR!') + if not os.path.exists(get.sfile): return public.return_msg_gettext(False,'Configuration file not exist') + if os.path.exists(get.dfile): return public.return_msg_gettext(False,'The specified soft link file name already exists') + l_name = os.path.basename(get.dfile) + if re.match(r"^[\w\-\.]+$", l_name) == None: return public.returnMsg(False, 'Link file name is illegal!') + if get.dfile[0] != '/': return public.return_msg_gettext(False,'The specified soft link file name must contain the full path (full path)') public.ExecShell("ln -sf {} {}".format(get.sfile,get.dfile)) - if not os.path.exists(get.dfile): return public.returnMsg(False,'SOFTLINK_CREATE_ERR') - public.WriteLog('TYPE_FIREWALL','CREATE_SOFTLINK',(get.dfile,get.sfile)) - return public.returnMsg(True,'SOFTLINK_CREATE_SUCCESS') + if not os.path.exists(get.dfile): return public.return_msg_gettext(False,'Softlink file creation failed') + public.write_log_gettext('Firewall manager','Create softlink: {} -> {}',(get.dfile,get.sfile)) + return public.return_msg_gettext(True,'The softlink file was created successfully') @@ -783,30 +860,30 @@ session.save_handler = files'''.format(path, sess_path, sess_path) get.path = get.path.encode('utf-8').strip() try: if get.path[-1] == '.': - return public.returnMsg(False, 'DIR_END_WITH1') + return public.return_msg_gettext(False, 'It is not recommended to use [ . ] at the end of the directory, because there may be safety risks') if not self.CheckFileName(get.path): - return public.returnMsg(False, 'DIR_NAME_SPECIAL_CHARACTRES') + return public.return_msg_gettext(False, 'Directory names cannot contain special characters!') if os.path.exists(get.path): - return public.returnMsg(False, 'DIR_EXISTS') + return public.return_msg_gettext(False, 'Requested directory exists!') os.makedirs(get.path) self.SetFileAccept(get.path) - public.WriteLog('TYPE_FILE', 'DIR_CREATE_SUCCESS', (get.path,)) - return public.returnMsg(True, 'DIR_CREATE_SUCCESS') + public.write_log_gettext('File manager', 'Successfully created directory!', (get.path,)) + return public.return_msg_gettext(True, 'Successfully created directory!') except: - return public.returnMsg(False,'DIR_CREATE_ERR') + return public.return_msg_gettext(False,'Failed to create directory!') #删除目录 def DeleteDir(self,get): if sys.version_info[0] == 2: get.path = get.path.encode('utf-8') - if get.path == '/www/Recycle_bin': - return public.returnMsg(False,'RECYCLE_BIN_ERR') + if os.path.basename(get.path) in ['Recycle_bin','.Recycle_bin']: + return public.return_msg_gettext(False,'Recovery failed!') if not os.path.exists(get.path): - return public.returnMsg(False, 'DIR_NOT_EXISTS') + return public.return_msg_gettext(False, 'Requested directory does not exist') # 检查是否敏感目录 if not self.CheckDir(get.path): - return public.returnMsg(False, 'FILE_DANGER') + return public.return_msg_gettext(False, 'Editing this directory may cause service exceptions!') try: # 检查是否存在.user.ini @@ -815,20 +892,22 @@ session.save_handler = files'''.format(path, sess_path, sess_path) public.ExecShell("chattr -R -i " + get.path) if hasattr(get, 'empty'): if not self.delete_empty(get.path): - return public.returnMsg(False, 'DIR_ERR_NOT_EMPTY') + return public.return_msg_gettext(False, 'Cannot delete non-empty directory!') if os.path.exists('data/recycle_bin.pl') and session.get('debug') != 1: if self.Mv_Recycle_bin(get): self.site_path_safe(get) - return public.returnMsg(True, 'DIR_MOVE_RECYCLE_BIN') + self.remove_file_ps(get) + return public.return_msg_gettext(True, 'Directory moved to 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') + public.WriteLog('TYPE_FILE', 'Successfully deleted directory [{}]!', (get.path,)) + self.remove_file_ps(get) + return public.return_msg_gettext(True, ' Successfully deleted directory!') except: - return public.returnMsg(False, 'DIR_DEL_ERR') + return public.return_msg_gettext(False, 'Failed to delete directory!') # 删除 空目录 def delete_empty(self, path): @@ -843,7 +922,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) if sys.version_info[0] == 2: get.path = get.path.encode('utf-8') if not os.path.exists(get.path): - return public.returnMsg(False, 'FILE_NOT_EXISTS') + return public.return_msg_gettext(False, 'Configuration file not exist') # 检查是否为.user.ini if get.path.find('.user.ini') != -1: @@ -852,91 +931,116 @@ session.save_handler = files'''.format(path, sess_path, sess_path) if os.path.exists('data/recycle_bin.pl') and session.get('debug') != 1: if self.Mv_Recycle_bin(get): self.site_path_safe(get) - return public.returnMsg(True, 'FILE_MOVE_RECYCLE_BIN') + self.remove_file_ps(get) + return public.return_msg_gettext(True, 'File moved to 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') + public.WriteLog('TYPE_FILE', 'Successfully deleted file [{}]!', (get.path,)) + self.remove_file_ps(get) + return public.return_msg_gettext(True, 'Successfully deleted file!') except: - return public.returnMsg(False, 'FILE_DEL_ERR') + return public.return_msg_gettext(False, 'Failed to delete file!') + + + def remove_file_ps(self,get): + ''' + @name 删除文件或目录的备注信息 + ''' + get.filename = get.path + get.ps_body = '' + get.ps_type = '0' + self.set_file_ps(get) # 移动到回收站 def Mv_Recycle_bin(self, get): - rPath = '/www/Recycle_bin/' - if not os.path.exists(rPath): - public.ExecShell('mkdir -p ' + rPath) - rFile = rPath + \ - get.path.replace('/', '_bt_') + '_t_' + str(time.time()) + rPath = public.get_recycle_bin_path(get.path) + rFile = os.path.join(rPath , get.path.replace('/', '_bt_') + '_t_' + str(time.time())) try: import shutil shutil.move(get.path, rFile) - public.WriteLog('TYPE_FILE', 'FILE_MOVE_RECYCLE_BIN', (get.path,)) + public.write_log_gettext('File manager', 'Successfully moved file [{}] to recycle bin!', (get.path,)) return True except: - public.WriteLog( - 'TYPE_FILE', 'FILE_MOVE_RECYCLE_BIN_ERR', (get.path,)) + public.write_log_gettext( + 'File manager', 'Failed to move file [{}] to recycle bin!', (get.path,)) return False # 从回收站恢复 def Re_Recycle_bin(self, get): - rPath = '/www/Recycle_bin/' if sys.version_info[0] == 2: get.path = get.path.encode('utf-8') + dFile = get.path.replace('_bt_', '/').split('_t_')[0] - get.path = rPath + get.path + + # 检查所在回收站目录 + recycle_bin_list = public.get_recycle_bin_list() + _ok = False + for r_path in recycle_bin_list: + for r_file in os.listdir(r_path): + if get.path == r_file: + _ok = True + rPath = r_path + get.path = os.path.join(rPath , get.path) + break + if _ok: break + if dFile.find('BTDB_') != -1: import database return database.database().RecycleDB(get.path) try: import shutil + if os.path.isdir(get.path) and os.path.exists(dFile): + shutil.move(dFile,dFile + "_{}.bak".format(public.format_date("%Y%m%d%H%M%S"))) shutil.move(get.path, dFile) - public.WriteLog('TYPE_FILE', 'FILE_RE_RECYCLE_BIN', (dFile,)) - return public.returnMsg(True, 'FILE_RE_RECYCLE_BIN') + public.write_log_gettext('File manager', 'Successfully recovered [{}] from recycle bin!', (dFile,)) + return public.return_msg_gettext(True, 'Recovery succeeded!') except: - public.WriteLog('TYPE_FILE', 'FILE_RE_RECYCLE_BIN_ERR', (dFile,)) - return public.returnMsg(False, 'FILE_RE_RECYCLE_BIN_ERR') + public.write_log_gettext('File manager', 'Failed to recover [{}] from recycle bin!', (dFile,)) + return public.return_msg_gettext(False, 'Recovery failed!') # 获取回收站信息 def Get_Recycle_bin(self, get): - rPath = '/www/Recycle_bin/' - if not os.path.exists(rPath): - public.ExecShell('mkdir -p ' + rPath) data = {} data['dirs'] = [] data['files'] = [] data['status'] = os.path.exists('data/recycle_bin.pl') data['status_db'] = os.path.exists('data/recycle_bin_db.pl') - for file in os.listdir(rPath): - file = self.xssencode(file) - try: - tmp = {} - fname = rPath + file - if sys.version_info[0] == 2: - fname = fname.encode('utf-8') - else: - fname.encode('utf-8') - tmp1 = file.split('_bt_') - tmp2 = tmp1[len(tmp1)-1].split('_t_') - tmp['rname'] = file - tmp['dname'] = file.replace('_bt_', '/').split('_t_')[0] - tmp['name'] = tmp2[0] - tmp['time'] = int(float(tmp2[1])) - if os.path.islink(fname): - filePath = os.readlink(fname) - if os.path.exists(filePath): - tmp['size'] = os.path.getsize(filePath) + recycle_bin_list = public.get_recycle_bin_list() + for rPath in recycle_bin_list: + if not os.path.exists(rPath): continue + for file in os.listdir(rPath): + file = self.xssencode(file) + try: + tmp = {} + fname = os.path.join(rPath , file) + if sys.version_info[0] == 2: + fname = fname.encode('utf-8') else: - tmp['size'] = 0 - else: - tmp['size'] = os.path.getsize(fname) - if os.path.isdir(fname): - if file[:5] == 'BTDB_': - tmp['size'] = public.get_path_size(fname) - data['dirs'].append(tmp) - else: - data['files'].append(tmp) - except: - continue + fname.encode('utf-8') + tmp1 = file.split('_bt_') + tmp2 = tmp1[len(tmp1)-1].split('_t_') + tmp['rname'] = file + tmp['dname'] = file.replace('_bt_', '/').split('_t_')[0] + if tmp['dname'].find('@') != -1: + tmp['dname'] = "BTDB_" + tmp['dname'][5:].replace('@',"\\u").encode().decode("unicode_escape") + tmp['name'] = tmp2[0] + tmp['time'] = int(float(tmp2[1])) + if os.path.islink(fname): + filePath = os.readlink(fname) + if os.path.exists(filePath): + tmp['size'] = os.path.getsize(filePath) + else: + tmp['size'] = 0 + else: + tmp['size'] = os.path.getsize(fname) + if os.path.isdir(fname): + if file[:5] == 'BTDB_': + tmp['size'] = public.get_path_size(fname) + data['dirs'].append(tmp) + else: + data['files'].append(tmp) + except: + continue data['dirs'] = sorted(data['dirs'],key = lambda x: x['time'],reverse=True) data['files'] = sorted(data['files'],key = lambda x: x['time'],reverse=True) @@ -944,19 +1048,31 @@ session.save_handler = files'''.format(path, sess_path, sess_path) # 彻底删除 def Del_Recycle_bin(self, get): - rPath = '/www/Recycle_bin/' if sys.version_info[0] == 2: get.path = get.path.encode('utf-8') + dFile = get.path.split('_t_')[0] - filename = rPath + get.path + # 检查所在回收站目录 + recycle_bin_list = public.get_recycle_bin_list() + _ok = False + for r_path in recycle_bin_list: + for r_file in os.listdir(r_path): + if get.path == r_file: + _ok = True + rPath = r_path + filename = os.path.join(rPath , get.path) + break + if _ok: break + + tfile = get.path.replace('_bt_', '/').split('_t_')[0] - if not os.path.exists(filename): - return public. returnMsg(True, 'FILE_DEL_RECYCLE_BIN', (tfile,)) + if not _ok: return public.returnMsg(False, 'Error deleting file : {}', (tfile,)) + if dFile.find('BTDB_') != -1: import database return database.database().DeleteTo(filename) if not self.CheckDir(filename): - return public.returnMsg(False, 'FILE_DANGER') + return public.return_msg_gettext(False, 'Never trouble troubles till troubles trouble you!') public.ExecShell('chattr -R -i ' + filename) if os.path.isdir(filename): @@ -964,45 +1080,50 @@ session.save_handler = files'''.format(path, sess_path, sess_path) try: shutil.rmtree(filename) except: + public.ExecShell('chattr -R -a ' + filename) public.ExecShell("rm -rf " + filename) else: try: os.remove(filename) except: public.ExecShell("rm -f " + filename) - public.WriteLog('TYPE_FILE', 'FILE_DEL_RECYCLE_BIN', (tfile,)) - return public.returnMsg(True, 'FILE_DEL_RECYCLE_BIN', (tfile,)) + public.write_log_gettext('File manager', 'Parmanently deleted {} from recycle bin!', (tfile,)) + return public.return_msg_gettext(True, 'Parmanently deleted {} from recycle bin!', (tfile,)) # 清空回收站 def Close_Recycle_bin(self, get): - rPath = '/www/Recycle_bin/' - public.ExecShell('chattr -R -i ' + rPath) + import database import shutil - rlist = os.listdir(rPath) - i = 0 - l = len(rlist) - for name in rlist: - i += 1 - path = rPath + name - public.writeSpeed(name, i, l) - if name.find('BTDB_') != -1: - database.database().DeleteTo(path) - continue - if os.path.isdir(path): - try: - shutil.rmtree(path) - except: - public.ExecShell('rm -rf ' + path) - else: - try: - os.remove(path) - except: - public.ExecShell('rm -f ' + path) + + recycle_bin_list = public.get_recycle_bin_list() + for rPath in recycle_bin_list: + public.ExecShell('chattr -R -i ' + rPath) + rlist = os.listdir(rPath) + i = 0 + l = len(rlist) + for name in rlist: + i += 1 + path = os.path.join(rPath , name) + public.writeSpeed(name, i, l) + if name.find('BTDB_') != -1: + database.database().DeleteTo(path) + continue + if os.path.isdir(path): + try: + shutil.rmtree(path) + except: + public.ExecShell('chattr -R -a ' + path) + public.ExecShell('rm -rf ' + path) + else: + try: + os.remove(path) + except: + public.ExecShell('rm -f ' + path) public.writeSpeed(None, 0, 0) - public.WriteLog('TYPE_FILE', 'FILE_CLOSE_RECYCLE_BIN') - return public.returnMsg(True, 'FILE_CLOSE_RECYCLE_BIN') + public.write_log_gettext('File manager', 'Recycle bin emptied!') + return public.return_msg_gettext(True, 'Recycle bin emptied!') # 回收站开关 def Recycle_bin(self, get): @@ -1011,12 +1132,12 @@ session.save_handler = files'''.format(path, sess_path, sess_path) c = 'data/recycle_bin_db.pl' if os.path.exists(c): os.remove(c) - public.WriteLog('TYPE_FILE', 'FILE_OFF_RECYCLE_BIN') - return public.returnMsg(True, 'FILE_OFF_RECYCLE_BIN') + public.write_log_gettext('File manager', 'Recycle bin feature turned off!') + return public.return_msg_gettext(True, 'Recycle bin feature turned off!') else: public.writeFile(c, 'True') - public.WriteLog('TYPE_FILE', 'FILE_ON_RECYCLE_BIN') - return public.returnMsg(True, 'FILE_ON_RECYCLE_BIN') + public.write_log_gettext('File manager', 'Recycle bin feature turned on!') + return public.return_msg_gettext(True, 'Recycle bin feature turned on!') # 复制文件 def CopyFile(self, get): @@ -1024,12 +1145,12 @@ session.save_handler = files'''.format(path, sess_path, sess_path) get.sfile = get.sfile.encode('utf-8') get.dfile = get.dfile.encode('utf-8') if get.dfile[-1] == '.': - return public.returnMsg(False, 'File_END_WITH') + return public.return_msg_gettext(False, 'It is not recommended to use [.] at the end of the file because there may be security risks') if not os.path.exists(get.sfile): - return public.returnMsg(False, 'FILE_NOT_EXISTS') + return public.return_msg_gettext(False, 'Configuration file not exist') # if os.path.exists(get.dfile): - # return public.returnMsg(False,'FILE_EXISTS') + # return public.return_msg_gettext(False,'Requested file exists!') if os.path.isdir(get.sfile): return self.CopyDir(get) @@ -1037,14 +1158,14 @@ session.save_handler = files'''.format(path, sess_path, sess_path) import shutil try: shutil.copyfile(get.sfile, get.dfile) - public.WriteLog('TYPE_FILE', 'FILE_COPY_SUCCESS', + public.write_log_gettext('File manager', 'Successfully copied file [{}] to [{}]!', (get.sfile, get.dfile)) stat = os.stat(get.sfile) os.chmod(get.dfile,stat.st_mode) os.chown(get.dfile, stat.st_uid, stat.st_gid) - return public.returnMsg(True, 'FILE_COPY_SUCCESS') + return public.return_msg_gettext(True, 'Successfully copied file!') except: - return public.returnMsg(False, 'FILE_COPY_ERR') + return public.return_msg_gettext(False, 'Failed to copy file!') # 复制文件夹 def CopyDir(self, get): @@ -1052,26 +1173,26 @@ session.save_handler = files'''.format(path, sess_path, sess_path) get.sfile = get.sfile.encode('utf-8') get.dfile = get.dfile.encode('utf-8') if get.dfile[-1] == '.': - return public.returnMsg(False, 'DIR_END_WITH1') + return public.return_msg_gettext(False, 'It is not recommended to use [.] at the end of the directory, because there may be safety risks') if not os.path.exists(get.sfile): - return public.returnMsg(False, 'DIR_NOT_EXISTS') + return public.return_msg_gettext(False, 'Requested directory does not exist') # if os.path.exists(get.dfile): - # return public.returnMsg(False,'DIR_EXISTS') + # return public.return_msg_gettext(False,'Requested directory exists!') # if not self.CheckDir(get.dfile): - # return public.returnMsg(False,'FILE_DANGER') + # return public.return_msg_gettext(False,'Never trouble troubles till troubles trouble you!') try: self.copytree(get.sfile, get.dfile) stat = os.stat(get.sfile) os.chmod(get.dfile,stat.st_mode) os.chown(get.dfile, stat.st_uid, stat.st_gid) - public.WriteLog('TYPE_FILE', 'DIR_COPY_SUCCESS', + public.write_log_gettext('File manager', 'Successfully copied directory!', (get.sfile, get.dfile)) - return public.returnMsg(True, 'DIR_COPY_SUCCESS') + return public.return_msg_gettext(True, 'Successfully copied directory!') except: - return public.returnMsg(False, 'DIR_COPY_ERR') + return public.return_msg_gettext(False, 'Failed to copy directory!') # 移动文件或目录 def MvFile(self, get): @@ -1079,37 +1200,38 @@ session.save_handler = files'''.format(path, sess_path, sess_path) get.sfile = get.sfile.encode('utf-8') get.dfile = get.dfile.encode('utf-8') if get.dfile[-1] == '.': - return public.returnMsg(False, 'File_END_WITH') + return public.return_msg_gettext(False, 'It is not recommended to use [.] at the end of the file because there may be security risks') if not self.CheckFileName(get.dfile): - return public.returnMsg(False,'FILE_NAME_SPECIAL_CHARACTRES') - if get.sfile == '/www/Recycle_bin': - return public.returnMsg(False,'RECYCLE_BIN_ERR') + return public.return_msg_gettext(False,'File names can NOT contain special characters!') + if os.path.basename(get.sfile) == '.Recycle_bin': + return public.return_msg_gettext(False,'Recovery failed!') if not os.path.exists(get.sfile): - return public.returnMsg(False, 'FILE_NOT_EXISTS') + return public.return_msg_gettext(False, 'Configuration file not exist') - if os.path.exists(get.dfile): - return public.returnMsg(False,'FILE_EXIST_ERR') + if hasattr(get, 'rename'): + if os.path.exists(get.dfile): + return public.return_msg_gettext(False,'The target file name already exists!') if get.dfile[-1] == '/': get.dfile = get.dfile[:-1] if get.dfile == get.sfile: - return public.returnMsg(False,'MEANINGLESS_OPERA') + return public.return_msg_gettext(False,'Meaningless operation') if not self.CheckDir(get.sfile): - return public.returnMsg(False,'FILE_DANGER') + return public.return_msg_gettext(False,'Never trouble troubles till troubles trouble you!') 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') + public.write_log_gettext('File manager','[{}] renamed to [{}]',(get.sfile,get.dfile)) + return public.return_msg_gettext(True,'Successfully renamed!') else: - public.WriteLog('TYPE_FILE', 'MOVE_SUCCESS', + public.write_log_gettext('File manager', 'Database moved!', (get.sfile, get.dfile)) - return public.returnMsg(True, 'MOVE_SUCCESS') + return public.return_msg_gettext(True, 'Database moved!') except: - return public.returnMsg(False, 'MOVE_ERR') + return public.return_msg_gettext(False, 'Failed to move file!') # 检查文件是否存在 def CheckExistsFiles(self, get): @@ -1148,16 +1270,24 @@ session.save_handler = files'''.format(path, sess_path, sess_path) get.path = get.path.encode('utf-8') get.path = self.xssdecode(get.path) + + if get.path.find('/rewrite/null/') != -1: + webserver = public.get_webserver() + get.path = get.path.replace("/rewrite/null/", "/rewrite/{}/".format(webserver)) + if get.path.find('/vhost/null/') != -1: + webserver = public.get_webserver() + get.path = get.path.replace("/vhost/null/", "/vhost/{}/".format(webserver)) + if not os.path.exists(get.path): if get.path.find('rewrite') == -1: - return public.returnMsg(False,'FILE_NOT_EXISTS',(get.path,)) + return public.return_msg_gettext(False,'Configuration file not exist') public.writeFile(get.path,'') if self.__get_ext(get.path) in ['gz','zip','rar','exe','db','pdf','doc','xls','docx','xlsx','ppt','pptx','7z','bz2','png','gif','jpg','jpeg','bmp','icon','ico','pyc','class','so','pyd']: - return public.returnMsg(False,'FILE_ONLINE_EDIT_ERR') + return public.return_msg_gettext(False,'The file format does not support online editing!') if os.path.getsize(get.path) > 3145928: - return public.returnMsg(False,'CANT_EDIT_ONLINE_FILE') + return public.return_msg_gettext(False,'Cannot edit files larger than 2MB online!') if os.path.isdir(get.path): - return public.returnMsg(False,'FILE_ERR') + return public.return_msg_gettext(False,'Writing verification file failed: {}') # 处理my.cnf为空的情况 myconf_file = '/etc/my.cnf' @@ -1187,30 +1317,54 @@ session.save_handler = files'''.format(path, sess_path, sess_path) data['encoding'] = 'BIG5' data['data'] = srcBody.decode(data['encoding']) except: - return public.returnMsg(False, 'FILE_ERR1') + return public.return_msg_gettext(False, 'File encoding is not compatible and cannot be read correctly!') else: - return public.returnMsg(False,'FILE_ERR2') + return public.return_msg_gettext(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) data['auto_save'] = self.get_auto_save(get.path) + data['st_mtime'] = str(int(os.stat(get.path).st_mtime)) return data except Exception as ex: - return public.returnMsg(False,'INCOMPATIBLE_FILECODE',(str(ex)),) + return public.return_msg_gettext(False,'The file cannot be read correctly due to incompatible file encoding!{}',(str(ex)),) #保存文件 def SaveFileBody(self,get): if not 'path' in get: - return public.returnMsg(False,'PATH_PARA_ERR') + return public.return_msg_gettext(False,'[path] parameter cannot be empty!') if sys.version_info[0] == 2: get.path = get.path.encode('utf-8') + + if get.path.find('/rewrite/null/') != -1: + webserver = public.get_webserver() + get.path = get.path.replace("/rewrite/null/", "/rewrite/{}/".format(webserver)) + if get.path.find('/vhost/null/') != -1: + webserver = public.get_webserver() + get.path = get.path.replace("/vhost/null/", "/vhost/{}/".format(webserver)) + if not os.path.exists(get.path): if get.path.find('.htaccess') == -1: - return public.returnMsg(False, 'FILE_NOT_EXISTS') + return public.return_msg_gettext(False, 'Configuration file not exist') + + nginx_conf_path = public.get_vhost_path() + '/nginx/' + if get.path.find(nginx_conf_path) != -1: + if get.data.find('#SSL-START') != -1 and get.data.find('#SSL-END') != -1: + if get.data.find('#error_page 404/404.html;') == -1: + str1 = public.get_msg_gettext('Failed to save the configuration file') + str2 = public.get_msg_gettext('Do not modify the 404 rule commented in the SSL config') + str3 = public.get_msg_gettext('To modify the 404 config, find the following config location') + + # return public.returnMsg(False,'Failed to save the configuration file:

                                      Do not modify the 404 rule commented in the SSL config

                                      To modify the 404 config, find the following config location:

                                      #ERROR-PAGE-START  Error page configuration, allowed to be commented
                                      ') + return public.get_msg_gettext(False,'{}:

                                      {}

                                      :

                                      #ERROR-PAGE-START  Error page configuration, allowed to be commented
                                      '.format(str1,str2,str3)) + + if 'st_mtime' in get: + st_mtime = str(int(os.stat(get.path).st_mtime)) + if st_mtime != get['st_mtime']: return public.returnMsg(False,'Failed to save, {} file has been changed, please refresh the content and modify it again.'.format(get.path)) his_path = '/www/backup/file_history/' if get.path.find(his_path) != -1: - return public.returnMsg(False,'HISTORY_DIR_ERR') + return public.return_msg_gettext(False,'Cannot modify history copy directly!') try: if 'base64' in get: import base64 @@ -1226,7 +1380,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) public.ExecShell('\\cp -a '+get.path+' /tmp/backup.conf') data = get.data - if data == 'undefined': return public.returnMsg(False,'FILE_ERR3') + if data == 'undefined': return public.return_msg_gettext(False,'Wrong file content, please save again!') userini = False if get.path.find('.user.ini') != -1: userini = True @@ -1260,20 +1414,20 @@ session.save_handler = files'''.format(path, sess_path, sess_path) isError = public.checkWebConfig() if isError != True: public.ExecShell('\\cp -a /tmp/backup.conf '+get.path) - return public.returnMsg(False, 'ERROR:
                                      '+isError.replace("\n", '
                                      ')+'
                                      ') + return public.return_msg_gettext(False, 'ERROR:
                                      '+isError.replace("\n", '
                                      ')+'
                                      ') public.serviceReload() if userini: public.ExecShell('chattr +i ' + get.path) - public.WriteLog('TYPE_FILE', 'FILE_SAVE_SUCCESS', (get.path,)) - return public.returnMsg(True, 'FILE_SAVE_SUCCESS') + public.write_log_gettext('File manager', 'Successfully saved file [{}]!', (get.path,)) + return public.return_msg_gettext(True, 'Saved!') except Exception as ex: - return public.returnMsg(False, 'FILE_SAVE_ERR' + str(ex)) + return public.return_msg_gettext(False, 'Save ERROR! {}' + str(ex)) # 保存历史副本 def save_history(self, filename): - if os.path.exists('/www/server/panel/data/not_file_history.pl'): + if os.path.exists(public.get_panel_path()+'/data/not_file_history.pl'): return True try: his_path = '/www/backup/file_history/' @@ -1286,7 +1440,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) his_list = sorted(os.listdir(save_path), reverse=True) num = public.readFile('data/history_num.pl') if not num: - num = 10 + num = 100 else: num = int(num) d_num = len(his_list) @@ -1334,7 +1488,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) args.filename).replace('//', '/') args.path = save_path + '/' + args.history if not os.path.exists(args.path): - return public.returnMsg(False,'HISTORY_ERR') + return public.return_msg_gettext(False,'The specified historical copy does not exist!') import shutil shutil.copyfile(args.path, args.filename) return self.GetFileBody(args) @@ -1349,9 +1503,9 @@ session.save_handler = files'''.format(path, sess_path, sess_path) f_md5 = public.FileMd5(filename) s_md5 = public.md5(args.body) if f_md5 == s_md5: - return public.returnMsg(True,'NOT_EDIT') + return public.return_msg_gettext(True,'Not Edit') public.writeFile(filename,args.body) - return public.returnMsg(True,'AUTO_SAVE') + return public.return_msg_gettext(True,'Automatically saved successfully!') # 取上一次自动保存的结果 def get_auto_save_body(self, args): @@ -1377,13 +1531,13 @@ session.save_handler = files'''.format(path, sess_path, sess_path) if get.z_type == 'rar': if os.uname().machine == 'aarch64': - return public.returnMsg(False,'RAR component does not support aarch 64 platform') + return public.return_msg_gettext(False,'RAR component does not support aarch 64 platform') import panelTask task_obj = panelTask.bt_task() - task_obj.create_task(public.GetMsg("COMPRESSION_FILE"),3,get.path,json.dumps({"sfile":get.sfile,"dfile":get.dfile,"z_type":get.z_type})) - public.WriteLog("TYPE_FILE", 'ZIP_SUCCESS',(get.sfile,get.dfile)) - return public.returnMsg(True,'ADD_COMPRESSION_TO_LINEUP') + task_obj.create_task(public.get_msg_gettext('Decompress the file'),3,get.path,json.dumps({"sfile":get.sfile,"dfile":get.dfile,"z_type":get.z_type})) + public.write_log_gettext("File manager", 'Successfully compressed file [{}] to [{}]!',(get.sfile,get.dfile)) + return public.return_msg_gettext(True,'Compression task added to the message queue!') # 文件解压 def UnZip(self, get): @@ -1391,9 +1545,9 @@ session.save_handler = files'''.format(path, sess_path, sess_path) if not 'password' in get: get.password = '' task_obj = panelTask.bt_task() - task_obj.create_task(public.GetMsg("DECOMPRESSION_FILE"),2,get.sfile,json.dumps({"dfile":get.dfile,"password":get.password})) - public.WriteLog("TYPE_FILE", 'UNZIP_SUCCESS',(get.sfile,get.dfile)) - return public.returnMsg(True,'ADD_DECOMPRESSION_TO_LINEUP') + task_obj.create_task(public.get_msg_gettext('Decompress the file'),2,get.sfile,json.dumps({"dfile":get.dfile,"password":get.password})) + public.write_log_gettext("File manager", 'Successfully uncompressed file from [{}] to [{}]!',(get.sfile,get.dfile)) + return public.return_msg_gettext(True,'Decompression task added to the message queue!') #获取文件/目录 权限信息 @@ -1420,17 +1574,17 @@ session.save_handler = files'''.format(path, sess_path, sess_path) all = '' try: if not self.CheckDir(get.filename): - return public.returnMsg(False, 'FILE_DANGER') + return public.return_msg_gettext(False, 'Never trouble troubles till troubles trouble you!') if not os.path.exists(get.filename): - return public.returnMsg(False, 'FILE_NOT_EXISTS') + return public.return_msg_gettext(False, 'Configuration file not exist') public.ExecShell('chmod '+all+' '+get.access+" '"+get.filename+"'") public.ExecShell('chown '+all+' '+get.user+':' + get.user+" '"+get.filename+"'") - public.WriteLog('TYPE_FILE', 'FILE_ACCESS_SUCCESS', + public.write_log_gettext('File manager', "Set [{}]'s permission to [{}] and authorized user to [{}]", (get.filename, get.access, get.user)) - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') except: - return public.returnMsg(False, 'SET_ERROR') + return public.return_msg_gettext(False, 'Failed to set') def SetFileAccept(self, filename): public.ExecShell('chown -R www:www ' + filename) @@ -1466,7 +1620,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) else: public.ExecShell('/etc/init.d/httpd reload') - public.WriteLog('TYPE_FILE', 'SITE_LOG_CLOSE') + public.write_log_gettext('File manager', 'Site Logs emptied!') get.path = public.GetConfigValue('logs_path') return self.GetDirSize(get) @@ -1476,7 +1630,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) get.path = get.path.encode('utf-8') if get.type == '1' or get.type == '2': session['selected'] = get - return public.returnMsg(True, 'FILE_ALL_TIPS') + return public.return_msg_gettext(True, 'Successfully marked, please click Paste All button in the target directory!') elif get.type == '3': for key in json.loads(get.data): try: @@ -1484,7 +1638,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) key = key.encode('utf-8') filename = get.path+'/'+key if not self.CheckDir(filename): - return public.returnMsg(False, 'FILE_DANGER') + return public.return_msg_gettext(False, 'Never trouble troubles till troubles trouble you!') ret = ' -R ' if 'all' in get: if get.all == 'False': @@ -1494,14 +1648,15 @@ session.save_handler = files'''.format(path, sess_path, sess_path) ':'+get.user+" '"+filename+"'") except: continue - public.WriteLog('TYPE_FILE', 'FILE_ALL_ACCESS') - return public.returnMsg(True, 'FILE_ALL_ACCESS') + public.write_log_gettext('File manager', 'Batch setting permission successful!') + return public.return_msg_gettext(True, 'Batch setting permission successful!') else: isRecyle = os.path.exists('data/recycle_bin.pl') and session.get('debug') != 1 path = get.path get.data = json.loads(get.data) l = len(get.data) i = 0 + args = public.dict_obj() for key in get.data: try: if sys.version_info[0] == 2: @@ -1514,7 +1669,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) public.writeSpeed(key, i, l) if os.path.isdir(filename): if not self.CheckDir(filename): - return public.returnMsg(False, 'FILE_DANGER') + return public.return_msg_gettext(False, 'Never trouble troubles till troubles trouble you!') public.ExecShell("chattr -R -i " + filename) if isRecyle: self.Mv_Recycle_bin(get) @@ -1530,12 +1685,18 @@ session.save_handler = files'''.format(path, sess_path, sess_path) self.Mv_Recycle_bin(get) else: os.remove(filename) + args.path = filename + self.remove_file_ps(args) 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') + if not isRecyle: + public.write_log_gettext('File manager', 'Batch deleting successful!') + return public.return_msg_gettext(True, 'Batch deleting successful!') + else: + public.write_log_gettext('File manager', '{} files or directories have been moved to the recycle bin in batches'.format(i)) + return public.return_msg_gettext(True, '{} files or directories have been moved to the recycle bin in batches'.format(i)) # 批量粘贴 def BatchPaste(self, get): @@ -1543,12 +1704,12 @@ session.save_handler = files'''.format(path, sess_path, sess_path) 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') + return public.return_msg_gettext(False,'Never trouble troubles till troubles trouble you!') if not 'selected' in session: - return public.returnMsg(False,'TCOPY_PRESS_ERR') + return public.return_msg_gettext(False,'TCOPY_PRESS_ERR') i = 0 if not 'selected' in session: - return public.returnMsg(False,'OPERA_FAILED') + return public.return_msg_gettext(False,'The operation failed, please re-operate') myfiles = json.loads(session['selected']['data']) l = len(myfiles) if get.type == '1': @@ -1563,7 +1724,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) dfile = get.path + '/' + key if dfile.find(sfile) == 0: - return public.returnMsg(False,'Wrong copy logic, from {} copy to {} has an inclusive relationship, there is an infinite loop copy risk!'.format(sfile,dfile)) + return public.return_msg_gettext(False,'Wrong copy logic, from {} copy to {} has an inclusive relationship, there is an infinite loop copy risk!'.format(sfile,dfile)) for key in myfiles: i += 1 @@ -1584,7 +1745,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) os.chown(dfile, stat.st_uid, stat.st_gid) except: continue - public.WriteLog('TYPE_FILE','FILE_ALL_COPY',(session['selected']['path'],get.path)) + public.write_log_gettext('File manager','Batch copied from [{}] to [{}]',(session['selected']['path'],get.path)) else: for key in myfiles: try: @@ -1600,11 +1761,11 @@ session.save_handler = files'''.format(path, sess_path, sess_path) except: continue self.site_path_safe(get) - public.WriteLog('TYPE_FILE','FILE_ALL_MOTE',(session['selected']['path'],get.path)) + public.write_log_gettext('File manager','Batch moved from [{}] to [{}]',(session['selected']['path'],get.path)) public.writeSpeed(None,0,0); errorCount = len(myfiles) - i del(session['selected']) - return public.returnMsg(True,'FILE_ALL',(str(i),str(errorCount))) + return public.return_msg_gettext(True,'Batch operating succeeded [{}], failed [{}]',(str(i),str(errorCount))) # 移动和重命名 def move(self, sfile, dfile): @@ -1660,7 +1821,7 @@ session.save_handler = files'''.format(path, sess_path, sess_path) def DownloadFile(self, get): import panelTask task_obj = panelTask.bt_task() - task_obj.create_task(public.GetMsg("DOWNLOAD_FILE"),1,get.url,get.path + '/' + get.filename) + task_obj.create_task(public.get_msg_gettext('Download file'),1,get.url,get.path + '/' + get.filename) #if sys.version_info[0] == 2: get.path = get.path.encode('utf-8'); #import db,time #isTask = '/tmp/panelTask.pl' @@ -1669,8 +1830,8 @@ session.save_handler = files'''.format(path, sess_path, sess_path) #sql.table('tasks').add('name,type,status,addtime,execstr',('下载文件['+get.filename+']','download','0',time.strftime('%Y-%m-%d %H:%M:%S'),execstr)) # public.writeFile(isTask,'True') # self.SetFileAccept(get.path+'/'+get.filename) - public.WriteLog('TYPE_FILE', 'FILE_DOWNLOAD', (get.url, get.path)) - return public.returnMsg(True, 'FILE_DOANLOAD') + public.write_log_gettext('File manager', 'Downloaded file [{}] to [{}]', (get.url, get.path)) + return public.return_msg_gettext(True, 'Download task added into the queue!') # 添加安装任务 def InstallSoft(self, get): @@ -1702,9 +1863,9 @@ session.save_handler = files'''.format(path, sess_path, sess_path) sql.table('tasks').add('id,name,type,status,addtime,execstr', (None, 'Install ['+get.name+'-'+get.version+']', 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr)) public.writeFile(isTask, 'True') - public.WriteLog('TYPE_SETUP', 'PLUGIN_ADD', (get.name, get.version)) + public.write_log_gettext('Installer', 'Download task added to queue!', (get.name, get.version)) time.sleep(0.1) - return public.returnMsg(True, 'PLUGIN_ADD') + return public.return_msg_gettext(True, 'Download task added to queue!') # 删除任务队列 def RemoveTask(self, get): @@ -1737,13 +1898,13 @@ done public.ExecShell('/etc/init.d/bt start') except: public.ExecShell('/etc/init.d/bt start') - return public.returnMsg(True, 'PLUGIN_DEL') + return public.return_msg_gettext(True, 'Task deleted') # 重新激活任务 def ActionTask(self, get): isTask = '/tmp/panelTask.pl' public.writeFile(isTask, 'True') - return public.returnMsg(True, 'PLUGIN_ACTION') + return public.return_msg_gettext(True, 'Task queue activated') # 卸载软件 def UninstallSoft(self, get): @@ -1760,16 +1921,16 @@ done default_ext.append("gmp") default_ext.append("opcache") if get.name.lower() in default_ext: - return public.returnMsg(False, "PHP_EXTENSION_UNINSTALL_ERR") + return public.return_msg_gettext(False, 'This extension is the default extension of OLS and cannot be uninstalled') execstr = "cd " + public.GetConfigValue('setup_path') + "/panel/install && /bin/bash install_soft.sh " + \ get.type+" uninstall " + get.name.lower() + " " + get.version.replace('.', '') if public.get_webserver() == "openlitespeed": execstr = "cd " + public.GetConfigValue('setup_path') + "/panel/install && /bin/bash install_soft.sh " + \ get.type + " uninstall " + get.name.lower() + "-ols " + get.version.replace('.', '') public.ExecShell(execstr) - public.WriteLog('TYPE_SETUP', 'PLUGIN_UNINSTALL', + public.write_log_gettext('Installer', 'Uninstallaton succeeded', (get.name, get.version)) - return public.returnMsg(True, "PLUGIN_UNINSTALL") + return public.return_msg_gettext(True, 'Uninstallaton succeeded') # 取任务队列进度 def GetTaskSpeed(self, get): @@ -1778,9 +1939,9 @@ done import db find = db.Sql().table('tasks').where('status=? OR status=?',('-1','0')).field('id,type,name,execstr').find() if(type(find) == str): - return public.returnMsg(False,"QUERY_ERR",(find,)) + return public.return_msg_gettext(False,'Query error, {}',(find,)) if not len(find): - return public.returnMsg(False,'NO_TASK_AT_LINEUP',("-2",)) + return public.return_msg_gettext(False,'NO_TASK_AT_LINEUP',("-2",)) isTask = '/tmp/panelTask.pl' public.writeFile(isTask, 'True') echoMsg = {} @@ -1790,12 +1951,12 @@ done try: tmp = public.readFile(tempFile) if len(tmp) < 10: - return public.returnMsg(False,'NO_TASK_AT_LINEUP',("-3",)) + return public.return_msg_gettext(False,'NO_TASK_AT_LINEUP',("-3",)) echoMsg['msg'] = json.loads(tmp) echoMsg['isDownload'] = True except: db.Sql().table('tasks').where("id=?",(find['id'],)).save('status',('0',)) - return public.returnMsg(False,'NO_TASK_AT_LINEUP',("-4",)) + return public.return_msg_gettext(False,'NO_TASK_AT_LINEUP',("-4",)) else: echoMsg['msg'] = self.GetLastLine(tempFile, 20) echoMsg['isDownload'] = False @@ -1812,7 +1973,7 @@ done def GetLastLine(self, inputfile, lineNum): result = public.GetNumLines(inputfile, lineNum) if len(result) < 1: - return public.getMsg('TASK_SLEEP') + return public.get_msg_gettext('Loading...') return result # 执行SHELL命令 @@ -1821,7 +1982,7 @@ done get.shell = get.shell.strip() tmp = get.shell.split(' ') if tmp[0] in disabled: - return public.returnMsg(False, 'FILE_SHELL_ERR', (tmp[0],)) + return public.return_msg_gettext(False, 'Sorry, [{}] command is NOT supported!', (tmp[0],)) shellStr = '''#!/bin/bash PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH @@ -1831,7 +1992,7 @@ cd %s public.writeFile('/tmp/panelShell.sh', shellStr) public.ExecShell( 'nohup bash /tmp/panelShell.sh > /tmp/panelShell.pl 2>&1 &') - return public.returnMsg(True, 'FILE_SHELL_EXEC') + return public.return_msg_gettext(True, 'Command sent') # 取SHELL执行结果 def GetExecShellMsg(self, get): @@ -1839,33 +2000,33 @@ cd %s if not os.path.exists(fileName): return 'FILE_SHELL_EMPTY' status = not public.process_exists('bash', None, '/tmp/panelShell.sh') - return public.returnMsg(status, public.GetNumLines(fileName, 200)) + return public.return_msg_gettext(status, public.GetNumLines(fileName, 200)) # 文件搜索 def GetSearch(self, get): if not os.path.exists(get.path): - return public.returnMsg(False, 'DIR_NOT_EXISTS') + return public.return_msg_gettext(False, 'Requested directory does not exist') return public.ExecShell("find "+get.path+" -name '*"+get.search+"*'") # 保存草稿 def SaveTmpFile(self, get): - save_path = '/www/server/panel/temp' + save_path = public.get_panel_path() + '/temp' if not os.path.exists(save_path): os.makedirs(save_path) get.path = os.path.join(save_path,public.Md5(get.path) + '.tmp') public.writeFile(get.path,get.body) - return public.returnMsg(True,'HAVE_BEEN_SAVE') + return public.return_msg_gettext(True,'Saved') # 获取草稿 def GetTmpFile(self, get): self.CleanOldTmpFile() - save_path = '/www/server/panel/temp' + save_path = public.get_panel_path() + '/temp' if not os.path.exists(save_path): os.makedirs(save_path) src_path = get.path get.path = os.path.join(save_path,public.Md5(get.path) + '.tmp') if not os.path.exists(get.path): - return public.returnMsg(False,'HAVE_NOT_DRAFT') + return public.return_msg_gettext(False,'No drafts available!') data = self.GetFileInfo(get.path) data['file'] = src_path if 'rebody' in get: @@ -1876,7 +2037,7 @@ cd %s def CleanOldTmpFile(self): if 'clean_tmp_file' in session: return True - save_path = '/www/server/panel/temp' + save_path = public.get_panel_path() + '/temp' max_time = 86400 * 30 now_time = time.time() for tmpFile in os.listdir(save_path): @@ -1899,8 +2060,8 @@ cd %s # 安装rar组件 def install_rar(self, get): - unrar_file = '/www/server/rar/unrar' - rar_file = '/www/server/rar/rar' + unrar_file = public.get_setup_path() + '/rar/unrar' + rar_file = public.get_setup_path() + '/rar/rar' bin_unrar = '/usr/local/bin/unrar' bin_rar = '/usr/local/bin/rar' if os.path.exists(unrar_file) and os.path.exists(bin_unrar): @@ -1919,8 +2080,8 @@ cd %s tmp_file = '/tmp/bt_rar.tar.gz' public.ExecShell('wget -O ' + tmp_file + ' ' + download_url) if os.path.exists(unrar_file): - public.ExecShell("rm -rf /www/server/rar") - public.ExecShell("tar xvf " + tmp_file + ' -C /www/server/') + public.ExecShell("rm -rf {}".format(rar_file)) + public.ExecShell("tar xvf " + tmp_file + ' -C {}'.format(public.get_setup_path())) if os.path.exists(tmp_file): os.remove(tmp_file) if not os.path.exists(unrar_file): @@ -1977,13 +2138,13 @@ cd %s def add_files_store(self, get): path = get.path if not os.path.exists(path): - return public.returnMsg(False,'FILE_DIR_NOT_EXIST') + return public.return_msg_gettext(False,'File or directory does not exist!') data = self.get_store_data() if path in data: - return public.returnMsg(False,'ADD_REPEATEDLY') + return public.return_msg_gettext(False,'Do not add it repeatedly!') data.append(path) self.set_store_data(data) - return public.returnMsg(True,'ADD_SUCCESS') + return public.return_msg_gettext(True,'Successfully added') #删除收藏夹 def del_files_store(self,get): @@ -1998,12 +2159,12 @@ cd %s is_go = True break if not is_go: - return public.returnMsg(False,'FAVORITE_NOT_FOUND') + return public.return_msg_gettext(False,'This favorite object could not be found!') data.remove(path) if len(data) <= 0: data = [] self.set_store_data(data) - return public.returnMsg(True,'DEL_SUCCESS') + return public.return_msg_gettext(True,'Successfully deleted') # #单文件木马扫描 # def file_webshell_check(self,get): @@ -2071,45 +2232,45 @@ cd %s # 获取指定下载地址 def get_download_url_find(self, get): - if not 'id' in get: return public.returnMsg(False, 'WRONG_PARAMETER') + if not 'id' in get: return public.return_msg_gettext(False, 'Wrong parameter') id = int(get.id) my_table = 'download_token' data = public.M(my_table).where('id=?', (id,)).find() - if not data: return public.returnMsg(False, 'ADDRESS_NOT_EXIST') + if not data: return public.return_msg_gettext(False, 'The specified address does not exist!') return data # 删除下载地址 def remove_download_url(self, get): - if not 'id' in get: return public.returnMsg(False, 'WRONG_PARAMETER') + if not 'id' in get: return public.return_msg_gettext(False, 'Wrong parameter') id = int(get.id) my_table = 'download_token' public.M(my_table).where('id=?', (id,)).delete() - return public.returnMsg(True, 'DEL_SUCCESS') + return public.return_msg_gettext(True, 'Successfully deleted!') # 修改下载地址 def modify_download_url(self, get): - if not 'id' in get: return public.returnMsg(False, 'WRONG_PARAMETER') + if not 'id' in get: return public.return_msg_gettext(False, 'Wrong parameter') id = int(get.id) my_table = 'download_token' if not public.M(my_table).where('id=?', (id,)).count(): - return public.returnMsg(False, 'ADDRESS_NOT_EXIST') + return public.return_msg_gettext(False, 'The specified address does not exist!') pdata = {} if 'expire' in get: pdata['expire'] = get.expire if 'password' in get: pdata['password'] = get.password if len(pdata['password']) < 4 and len(pdata['password']) > 0: - return public.returnMsg(False,'The length of the extracted password cannot be less than 4 digits') + return public.return_msg_gettext(False,'The length of the extracted password cannot be less than 4 digits') if not re.match('^\w+$',pdata['password']): - return public.returnMsg(False,'The password only supports a combination of uppercase and lowercase letters and numbers') + return public.return_msg_gettext(False,'The password only supports a combination of uppercase and lowercase letters and numbers') if 'ps' in get: pdata['ps'] = get.ps public.M(my_table).where('id=?', (id,)).update(pdata) - return public.returnMsg(True, 'EDIT_SUCCESS') + return public.return_msg_gettext(True, 'Successfully modified') # 生成下载地址 def create_download_url(self, get): if not os.path.exists(get.filename): - return public.returnMsg(False,'FILE_DIR_NOT_EXIST') + return public.return_msg_gettext(False,'File or directory does not exist!') my_table = 'download_token' mtime = int(time.time()) pdata = { @@ -2122,13 +2283,13 @@ cd %s "addtime": mtime #添加时间 } if len(pdata['password']) < 4 and len(pdata['password']) > 0: - return public.returnMsg(False,'PASSWD_ERR') + return public.return_msg_gettext(False,' Please do not enter the following special characters [ ~ ` / = ]') if not re.match('^\w+$',pdata['password']) and pdata['password']: - return public.returnMsg(False,'The password only supports a combination of uppercase and lowercase letters and numbers') + return public.return_msg_gettext(False,'The password only supports a combination of uppercase and lowercase letters and numbers') #更新 or 插入 token = public.M(my_table).where('filename=?',(get.filename,)).getField('token') if token: - return public.returnMsg(False, 'ALREADY_SHARED') + return public.return_msg_gettext(False, 'Already shared!') # pdata['token'] = token # del(pdata['total']) # public.M(my_table).where('token=?',(token,)).update(pdata) @@ -2136,12 +2297,12 @@ cd %s id = public.M(my_table).insert(pdata) pdata['id'] = id - return public.returnMsg(True, pdata) + return public.return_msg_gettext(True, pdata) #取PHP-CLI执行命令 def __get_php_bin(self,php_version=None): - php_vs = ["80","74","73","72","71","70","56","55","54","53"] + php_vs = public.get_php_versions(True) if php_version: if php_version != 'auto': if not php_version in php_vs: return '' @@ -2179,7 +2340,7 @@ cd %s public.ExecShell("nohup {} &> /tmp/panelExec.pl &".format(get.giturl)) else: public.ExecShell("nohup git clone {} &> /tmp/panelExec.pl &".format(get.giturl)) - return public.returnMsg(True,'COMMAND_SENT') + return public.return_msg_gettext(True,'Command has been sent!') # 安装composer def get_composer_bin(self): @@ -2200,7 +2361,7 @@ cd %s #准备执行环境 composer_bin = self.get_composer_bin() if not composer_bin: - return public.returnMsg(False,'NO_COMPOSER_AVAILABLE') + return public.return_msg_gettext(False,'No composer available!') #取执行PHP版本 php_version = None @@ -2208,11 +2369,11 @@ cd %s php_version = get.php_version php_bin = self.__get_php_bin(php_version) if not php_bin: - return public.returnMsg(False,'PHP_VER_NOT_FOUND') + return public.return_msg_gettext(False,'No available PHP version was found, or the specified PHP version was not installed!') get.composer_cmd = get.composer_cmd.strip() if get.composer_cmd == '': if not os.path.exists(get.path + '/composer.json'): - return public.returnMsg(False,'COMPOSER_CONF_NOT_FOUND') + return public.return_msg_gettext(False,'The composer.json configuration file was not found in the specified directory!') log_file = '/tmp/composer.log' user = '' if 'user' in get: @@ -2242,14 +2403,14 @@ cd %s if os.path.exists(log_file): os.remove(log_file) public.ExecShell("cd {} && export COMPOSER_HOME=/tmp && {} nohup {} &> {} && echo 'BT-Exec-Completed' >> {} && rm -rf /home/www &".format(get.path,user,composer_exec_str,log_file,log_file)) - public.WriteLog('Composer',"EXEC_COMPOSER",(get.path,get.composer_args)) - return public.returnMsg(True,'COMMAND_SENT') + public.write_log_gettext('Composer',"Execute composer [{}] in the directory: [{}]",(get.path,get.composer_args)) + return public.return_msg_gettext(True,'Command has been sent!') # 取composer版本 def get_composer_version(self,get): composer_bin = self.get_composer_bin() if not composer_bin: - return public.returnMsg(False,'NO_COMPOSER_AVAILABLE') + return public.return_msg_gettext(False,'No composer available!') try: bs = str(public.readFile(composer_bin,'rb')) @@ -2257,28 +2418,28 @@ cd %s if not result: raise Exception('empty!') except: php_bin = self.__get_php_bin() - if not php_bin: return public.returnMsg(False,'No available PHP version found!') + if not php_bin: return public.return_msg_gettext(False,'No available PHP version found!') composer_exec_str = 'export COMPOSER_HOME=/tmp && ' + php_bin + ' ' + composer_bin +' --version 2>/dev/null|grep \'Composer version\'|awk \'{print $3}\'' result = public.ExecShell(composer_exec_str)[0].strip() - data = public.returnMsg(True,result) + data = public.return_msg_gettext(True,result) if 'path' in get: import panelSite data['php_versions'] = panelSite.panelSite().GetPHPVersion(get) data['comp_json'] = True data['comp_lock'] = False if not os.path.exists(get.path + '/composer.json'): - data['comp_json'] = public.getMsg('COMPOSER_CONF_NOT_EXIST') + data['comp_json'] = public.get_msg_gettext('[Composer.json] configuration file is not found in the specified directory!') if os.path.exists(get.path + '/composer.lock'): - data['comp_lock'] = public.getMsg('COMPOSERLOGCK_EXIST') + data['comp_lock'] = public.get_msg_gettext('[Composer.lock] file exists in the specified directory, please delete it before executing') return data # 升级composer版本 def update_composer(self,get): composer_bin = self.get_composer_bin() if not composer_bin: - return public.returnMsg(False,'NO_COMPOSER_AVAILABLE') + return public.return_msg_gettext(False,'No composer available!') php_bin = self.__get_php_bin() - if not php_bin: return public.returnMsg(False,'No available PHP version found!') + if not php_bin: return public.return_msg_gettext(False,'No available PHP version found!') #设置指定源 # if 'repo' in get: # if get.repo: @@ -2289,11 +2450,11 @@ cd %s public.ExecShell(composer_exec_str) version2 = self.get_composer_version(get)['msg'] if version1 == version2: - msg = public.getMsg("COMPOSER_UPDATE_ERR") + msg = public.get_msg_gettext('Currently the latest version, no upgrade required!') else: - msg = public.getMsg("COMPOSER_UPDATE",(version1,version2)) - public.WriteLog('Composer',msg) - return public.returnMsg(True,msg) + msg = public.get_msg_gettext('Upgrade composer from {} to {}',(version1,version2)) + public.write_log_gettext('Composer',msg) + return public.return_msg_gettext(True,msg) # 计算文件HASH def get_file_hash(self,args=None,filename=None): @@ -2380,7 +2541,7 @@ cd %s def get_file_attribute(self,args): filename = args.filename.strip() if not os.path.exists(filename): - return public.returnMsg(False,'the specified file does not exist!') + return public.return_msg_gettext(False,'File does not exist!') attribute = {} attribute['name'] = os.path.basename(filename) attribute['path'] = os.path.dirname(filename) @@ -2398,8 +2559,8 @@ cd %s attribute['user'] = self.get_mode_user(f_stat.st_uid) # 所属用户 attribute['group'] = self.get_mode_user(f_stat.st_gid) # 所属组 attribute['mode'] = str(oct(f_stat.st_mode)[-3:]) # 文件权限号 - attribute['md5'] = 'Do not count files or directories larger than 100MB' # 文件MD5 - attribute['sha1'] = 'Do not count files or directories larger than 100MB' # 文件sha1 + attribute['md5'] = public.get_msg_gettext('Do not count files or directories larger than 100MB') # 文件MD5 + attribute['sha1'] = public.get_msg_gettext('Do not count files or directories larger than 100MB') # 文件sha1 attribute['lsattr'] = self.get_lsattr(filename) attribute['is_dir'] = os.path.isdir(filename) # 是否为目录 attribute['is_link'] = os.path.islink(filename) # 是否为链接文件 @@ -2433,6 +2594,119 @@ cd %s adad=panelSearch.panelSearch() return adad.get_replace_logs(args) + def get_path_images(self, path): + ''' + @name 获取目录的图片列表 + @param path 目录路径 + @return 图片列表 + ''' + image_list = [] + for fname in os.listdir(path): + if fname.split('.')[-1] in ['png', 'jpeg', 'gif', 'jpg', 'bmp', 'ico']: + image_list.append(fname) + return ','.join(image_list) + + def clear_thumbnail(self): + ''' + @name 清除过期的缩略图缓存 + @author hwliang + @return void + ''' + try: + from BTPanel import cache + except: + return + ikey = 'thumbnail_cache' + if cache.get(ikey): return + + cache_path = '{}/cache/thumbnail'.format(public.get_panel_path()) + if not os.path.exists(cache_path): return + expire_time = time.time() - (30 * 86400) # 30天前的文件 + for fname in os.listdir(cache_path): + filename =os.path.join(cache_path,fname) + if os.path.getctime(filename) < expire_time: + os.remove(filename) + + # 标记,每天清理一次 + cache.set(ikey,1,86400) + + + + + def get_images_resize(self,args): + ''' + @name 获取指定图片的缩略图 + @author hwliang<2022-03-02> + @param args{ + "path": "", 图片路径 + "files": xx.png,aaa.jpg, 文件名称(不包含目录路径),如果files=*,则返回该目录下的所有图片 + "width": 50, 宽 + "heigth:50, 高 + "return_type": "base64" // base64,file + } + @return base64编码的图片 or file + ''' + from PIL import Image + from base64 import b64encode + from io import BytesIO + if args.files == '*': + args.files = self.get_path_images(args.path) + + file_list = args.files.split(',') + + width = int(args.width) + height = int(args.height) + + cache_path = '{}/cache/thumbnail'.format(public.get_panel_path()) + if not os.path.exists(cache_path): os.makedirs(cache_path,384) + data = {} + _max_time = 3 # 最大处理时间 + _stime = time.time() + + # 清理过期的缩略图缓存 + self.clear_thumbnail() + + for fname in file_list: + try: + filename = os.path.join(args.path,fname) + f_size = os.path.getsize(filename) + cache_file = os.path.join(cache_path,public.md5("{}_{}_{}_{}".format(filename,width,height,f_size))) + if not os.path.exists(filename): + # 移除缓存文件 + if os.path.exists(cache_file): os.remove(cache_file) + continue + + # 有缩略图缓存的使用缓存 + if os.path.exists(cache_file): + data[fname] = public.readFile(cache_file) + continue + + # 超出最大处理时间直接跳过后续图片的处理,以免影响前端用户体验 + if time.time() - _stime > _max_time: + data[fname] = '' + continue + + im = Image.open(filename) + im.thumbnail((width,height)) + out = BytesIO() + im.save(out, im.format) + out.seek(0) + image_type = im.format.lower() + mimetype = 'image/{}'.format(image_type) + if args.return_type == 'base64': + b64_data = "data:{};base64,".format(mimetype) + b64encode(out.read()).decode('utf-8') + data[fname] = b64_data + out.close() + # 写缩略图缓存 + public.writeFile(cache_file,b64_data) + else: + from flask import send_file + return send_file(out, mimetype=mimetype, cache_timeout=0) + except: + data[fname] = '' + + return public.return_data(True,data) + # 数据库对象 def _get_sqlite_connect(self): try: @@ -2510,7 +2784,6 @@ CREATE TABLE index_tb( for n_tb in tb_names: if n_tb not in exist_tb: tb_name.append(n_tb) - print(tb_name) break if not tb_name: tb_name.append(tb_names[0]) @@ -2582,9 +2855,9 @@ CREATE TABLE index_tb( def back_path_permissions(self,get): back_limit = 100 if self._get_total_back() >= back_limit: - return public.returnMsg(False,"The number of backup versions has exceeded {} ,Please go to the upper right corner [Backup Permissions] to clean up the old backup before operating".format(back_limit)) + return public.return_msg_gettext(False,"The number of backup versions has exceeded {} ,Please go to the upper right corner [Backup Permissions] to clean up the old backup before operating".format(back_limit)) if not os.path.exists(get.path): - return public.returnMsg(False,"Path is incorrect {}".format(get.path)) + return public.return_msg_gettext(False,"Path is incorrect {}".format(get.path)) path = get.path back_sub_dir = get.back_sub_dir remark = get.remark @@ -2599,12 +2872,12 @@ CREATE TABLE index_tb( else: self.back_single_file_perm(path,date,remark,tb_name) except Exception as e: - return public.returnMsg(False,"Backup error {} ".format(e)) + return public.return_msg_gettext(False,"Backup error {} ".format(e)) finally: self.sqlite_connection.commit() self.sqlite_connection.close() self.sqlite_connection=None - return public.returnMsg(True,"Backup succeeded") + return public.return_msg_gettext(True,"Backup succeeded!") # 获取所有需要还原文件和文件夹 def _get_restore_file(self,path): @@ -2651,13 +2924,11 @@ CREATE TABLE index_tb( def _restore_dir_perm(self,path_full,restore_sub_dir,date): tb_name = self._operate_db("select permissions_tb from TB_NAME where date='{}'".format(date)).fetchall() main_dir_data = self._operate_db("select path,owner,mode from TB_NAME where path='{}'".format(path_full),permissions_tb=tb_name[0][0]).fetchall() - print(main_dir_data) if main_dir_data: public.set_mode(main_dir_data[0][0], main_dir_data[0][2]) public.set_own(main_dir_data[0][0], main_dir_data[0][1]) if restore_sub_dir == "0": - print(main_dir_data[0][0]) - public.returnMsg(True, "Permission restored successfully") + public.return_msg_gettext(True, "Permission restored successfully") self._get_restore_file(path_full) if tb_name: data = self._operate_db("select path,owner,mode from TB_NAME",permissions_tb=tb_name[0][0]).fetchall() @@ -2667,7 +2938,7 @@ CREATE TABLE index_tb( if d[0] in self.file_permission_list: public.set_mode(d[0], d[2]) public.set_own(d[0], d[1]) - return public.returnMsg(True, "Permission restored successfully") + return public.return_msg_gettext(True, "Permission restored successfully") # 还原单个文件权限 def restore_single_file_perm(self,path_full,date): @@ -2676,8 +2947,8 @@ CREATE TABLE index_tb( if main_dir_data: public.set_mode(main_dir_data[0][0], main_dir_data[0][2]) public.set_own(main_dir_data[0][0], main_dir_data[0][1]) - return public.returnMsg(True, "Permission restored successfully") - return public.returnMsg(False, "The file does not have backup permissions") + return public.return_msg_gettext(True, "Permission restored successfully") + return public.return_msg_gettext(False, "The file does not have backup permissions") # 还原权限 @@ -2704,6 +2975,11 @@ CREATE TABLE index_tb( for tb_name in exist_tbs: data = self._operate_db("select path,owner,mode,date from TB_NAME where path='{}'".format(path_full), permissions_tb=tb_name).fetchall() + if not data and os.path.isdir(path_full) and path_full[-1] != "/": + path_full += "/" + data = self._operate_db( + "select path,owner,mode,date from TB_NAME where path='{}'".format(path_full), + permissions_tb=tb_name).fetchall() if data: index_data = self._operate_db("select id,remark from index_tb where permissions_tb='{}'".format(tb_name)).fetchall() d_l = [] @@ -2723,7 +2999,7 @@ CREATE TABLE index_tb( self._operate_db("drop table '{}'".format(p_tb[0][0])) self.sqlite_connection.commit() self.sqlite_connection.close() - return public.returnMsg(True, "successfully deleted") + return public.return_msg_gettext(True, "Successfully deleted!") # 获取所有备份 def get_all_back(self,get): @@ -2744,7 +3020,7 @@ CREATE TABLE index_tb( if os.path.isfile(path): os.chown(path, get.uid, get.gid) os.chmod(path, 0o644) - return public.returnMsg(True, "Permission repair succeeded") + return public.return_msg_gettext(True, "Permission repair succeeded") os.chown(path, get.uid, get.gid) os.chmod(path, 0o755) for file in os.listdir(path): @@ -2759,7 +3035,7 @@ CREATE TABLE index_tb( os.chmod(filename,0o644) except: print(public.get_error_info()) - return public.returnMsg(True,"Permission repair succeeded") + return public.return_msg_gettext(True,"Permission repair succeeded") def restore_website(self,args): """ diff --git a/class/firewall_new.py b/class/firewall_new.py index c102ba3b..707b21ef 100644 --- a/class/firewall_new.py +++ b/class/firewall_new.py @@ -139,9 +139,9 @@ class firewalls: import time import re rep = "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$" - if not re.search(rep,get.port): return public.returnMsg(False,'FIREWALL_IP_FORMAT'); + if not re.search(rep,get.port): return public.return_msg_gettext(False,'IP address youve entered is illegal!'); address = get.port - if public.M('firewall').where("port=?",(address,)).count() > 0: return public.returnMsg(False,'FIREWALL_IP_EXISTS') + if public.M('firewall').where("port=?",(address,)).count() > 0: return public.return_msg_gettext(False,'The IP exists in block list, no need to repeat processing!') if self.__isUfw: public.ExecShell('ufw deny from ' + address + ' to any'); else: @@ -154,11 +154,11 @@ class firewalls: else: public.ExecShell('iptables -I INPUT -s '+address+' -j DROP') - public.WriteLog("TYPE_FIREWALL", 'FIREWALL_DROP_IP',(address,)) + public.write_log_gettext("Firewall manager", 'Successfully blocked IP [{}]!',(address,)) addtime = time.strftime('%Y-%m-%d %X',time.localtime()) public.M('firewall').add('port,ps,addtime',(address,get.ps,addtime)) self.FirewallReload() - return public.returnMsg(True,'ADD_SUCCESS') + return public.return_msg_gettext(True,'Successfully added') @@ -178,11 +178,11 @@ class firewalls: else: public.ExecShell('iptables -D INPUT -s '+address+' -j DROP') - public.WriteLog("TYPE_FIREWALL",'FIREWALL_ACCEPT_IP',(address,)) + public.write_log_gettext("Firewall manager",'Unblocked IP [{}]!',(address,)) public.M('firewall').where("id=?",(id,)).delete() self.FirewallReload(); - return public.returnMsg(True,'DEL_SUCCESS') + return public.return_msg_gettext(True,'Successfully deleted') #添加放行端口 @@ -190,28 +190,28 @@ class firewalls: flag=False import re rep = "^\d{1,5}(:\d{1,5})?$" - if not re.search(rep,get.port): return public.returnMsg(False,'PORT_CHECK_RANGE'); + if not re.search(rep,get.port): return public.return_msg_gettext(False,'Port range is incorrect!'); import time port = get.port ps = get.ps types=get.type type_list=['tcp','udp'] - if types not in type_list:return public.returnMsg(False, 'FIREWALL_PORT_EXISTS') - notudps = ['80', '443', '8888', '888', '39000:40000', '21', '22', '7800'] + if types not in type_list:return public.return_msg_gettext(False, 'The port exists, no need to repeat the release!') + notudps = ['80', '443', '8888', '888', '39000:40000', '21', '22'] if port in notudps:flag=True #return public.M('firewall').where("port=?", (port,)).count() if types=='tcp': if flag: - if public.M('firewall').where("port=?", (port,)).count() > 0: return public.returnMsg(False, 'FIREWALL_PORT_EXISTS') + if public.M('firewall').where("port=?", (port,)).count() > 0: return public.return_msg_gettext(False, 'The port exists, no need to repeat the release!') else: - if public.M('firewall').where("port=? and type='tcp'",(port,)).count() > 0: return public.returnMsg(False,'FIREWALL_PORT_EXISTS') + if public.M('firewall').where("port=? and type='tcp'",(port,)).count() > 0: return public.return_msg_gettext(False,'The port exists, no need to repeat the release!') elif types=='udp': if flag: - if public.M('firewall').where("port=?", (port,)).count() > 0: return public.returnMsg( False, 'FIREWALL_PORT_EXISTS') + if public.M('firewall').where("port=?", (port,)).count() > 0: return public.return_msg_gettext( False, 'The port exists, no need to repeat the release!') else: - if public.M('firewall').where("port=? and type='udp'", (port,)).count() > 0: return public.returnMsg(False,'FIREWALL_PORT_EXISTS') + if public.M('firewall').where("port=? and type='udp'", (port,)).count() > 0: return public.return_msg_gettext(False,'The port exists, no need to repeat the release!') else: - return public.returnMsg(False, 'FIREWALL_PORT_EXISTS') + return public.return_msg_gettext(False, 'The port exists, no need to repeat the release!') if self.__isUfw: if port in notudps: @@ -231,12 +231,12 @@ class firewalls: else: public.ExecShell('iptables -I INPUT -p tcp -m state --state NEW -m ' + types +' --dport ' + port + ' -j ACCEPT' ) - public.WriteLog("TYPE_FIREWALL", 'FIREWALL_ACCEPT_PORT',(port,)) + public.write_log_gettext("Firewall manager", 'Successfully accepted port [{}]!',(port,)) addtime = time.strftime('%Y-%m-%d %X',time.localtime()) result = public.M('firewall').add('port,ps,addtime,types',(port,ps,addtime,types)) #return result self.FirewallReload() - return public.returnMsg(True,'ADD_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #删除放行端口 @@ -245,9 +245,9 @@ class firewalls: id = get.id types=get.type type_list = ['tcp', 'udp'] - if not types in type_list: return public.returnMsg(False, 'FIREWALL_PORT_EXISTS') + if not types in type_list: return public.return_msg_gettext(False, 'The port exists, no need to repeat the release!') try: - if(port == public.GetHost(True)): return public.returnMsg(False,'FIREWALL_PORT_PANEL') + if(port == public.GetHost(True)): return public.return_msg_gettext(False,'Failed,cannot delete current port of the panel!') if self.__isUfw: public.ExecShell('ufw delete allow ' + port + '/' + types+ ''); else: @@ -255,13 +255,13 @@ class firewalls: public.ExecShell('firewall-cmd --permanent --zone=public --remove-port='+port+'/' + types + '') else: public.ExecShell('iptables -D INPUT -p tcp -m state --state NEW -m ' + types +' --dport '+port+' -j ACCEPT') - public.WriteLog("TYPE_FIREWALL", 'FIREWALL_DROP_PORT',(port,)) + public.write_log_gettext("Firewall manager", 'Successfully deleted accepted port [{}] on firewall!',(port,)) public.M('firewall').where("id=?",(id,)).delete() self.FirewallReload() - return public.returnMsg(True,'DEL_SUCCESS') + return public.return_msg_gettext(True,'Successfully deleted') except: - return public.returnMsg(False,'DEL_ERROR') + return public.return_msg_gettext(False,'Failed to delete') @@ -269,10 +269,10 @@ class firewalls: def SetSshStatus(self,get): version = public.readFile('/etc/redhat-release') if int(get['status'])==1: - msg = public.getMsg('FIREWALL_SSH_STOP') + msg = public.get_msg_gettext('SSH service turned off') act = 'stop' else: - msg = public.getMsg('FIREWALL_SSH_START') + msg = public.get_msg_gettext('SSH service turned on') act = 'start' if not os.path.exists('/etc/redhat-release'): @@ -282,8 +282,8 @@ class firewalls: else: public.ExecShell("/etc/init.d/sshd "+act) - public.WriteLog("TYPE_FIREWALL", msg) - return public.returnMsg(True,'SUCCESS') + public.write_log_gettext("Firewall manager", msg) + return public.return_msg_gettext(True,'Setup successfully!') @@ -305,7 +305,7 @@ class firewalls: public.writeFile(filename,conf) public.ExecShell('sysctl -p') - return public.returnMsg(True,'SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') @@ -313,9 +313,9 @@ class firewalls: def SetSshPort(self,get): #return public.returnMsg(False,'演示服务器,禁止此操作!'); port = get.port - if int(port) < 22 or int(port) > 65535: return public.returnMsg(False,'FIREWALL_SSH_PORT_ERR'); - ports = ['21','25','80','443','8080','888','8888', '7800'] - if port in ports: return public.returnMsg(False,''); + if int(port) < 22 or int(port) > 65535: return public.return_msg_gettext(False,'Port range must be between 22 and 65535!'); + ports = ['21','25','80','443','8080','888','8888']; + if port in ports: return public.return_msg_gettext(False,''); file = '/etc/ssh/sshd_config' conf = public.readFile(file) @@ -337,9 +337,9 @@ class firewalls: public.ExecShell("/etc/init.d/sshd restart") self.FirewallReload() - public.M('firewall').where("ps=?",(public.GetMsg("SSH_SERVER"),)).setField('port',port) - public.WriteLog("TYPE_FIREWALL", "FIREWALL_SSH_PORT",(port,)) - return public.returnMsg(True,'EDIT_SUCCESS') + public.M('firewall').where("ps=?",(public.get_msg_gettext('SSH Server'),)).setField('port',port) + public.write_log_gettext("Firewall manager", "Successfully changed SSH port to [{}]!",(port,)) + return public.return_msg_gettext(True,'Setup successfully!') #取SSH信息 def GetSshInfo(self,get): @@ -398,11 +398,11 @@ class firewalls: import re # 判断端口是否正确 rep = "^\d{1,5}(:\d{1,5})?$" - if not re.search(rep, get.port): return public.returnMsg(False, 'PORT_CHECK_RANGE'); + if not re.search(rep, get.port): return public.return_msg_gettext(False, 'Port range is incorrect! should be between 100-65535'); # 判断IP是否正确 rep2 = "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$" - if not re.search(rep2, get.address): return public.returnMsg(False, 'FIREWALL_IP_FORMAT'); + if not re.search(rep2, get.address): return public.return_msg_gettext(False, 'IP address is illegal!'); import time ports = get.port ps = get.ps @@ -414,19 +414,19 @@ class firewalls: type_list=['reject','accept'] # 判断type类型是否正确 - if types not in type_list:return public.returnMsg(False, 'FIREWALL_PORT_EXISTS') + if types not in type_list:return public.return_msg_gettext(False, 'The port exists, no need to repeat the release!') # 判断protocol 类型是否正确 - if protocol not in protocol_list: return public.returnMsg(False, 'FIREWALL_PORT_EXISTS') + if protocol not in protocol_list: return public.return_msg_gettext(False, 'The port exists, no need to repeat the release!') - notudps = ['80', '443', '8888', '888', '39000:40000', '21', '22','7800'] + notudps = ['80', '443', '8888', '888', '39000:40000', '21', '22'] if ports in notudps: flag = True # sql 查询 #sql="select * from firewall where ports='%s' and address_ip='%s' and protocol='%s' and types='%s';" % (str(ports), str(address_ip), str(protocol), str(types)) query_result = public.M('firewall').where('ports=? and address_ip=? and protocol=? and types=?',(ports, address_ip, protocol, types)).count() # 这里大于0 表示存在 - if query_result > 0 : return public.returnMsg(False,'FIREWALL_PORT_EXISTS') + if query_result > 0 : return public.return_msg_gettext(False,'The port exists, no need to repeat the release!') if self.__isUfw: if type=='accept': @@ -447,11 +447,11 @@ class firewalls: 'iptables -I INPUT -s ' + address_ip + ' -p ' + protocol + ' --dport ' + ports + ' -j DROP') - public.WriteLog("TYPE_FIREWALL", 'FIREWALL_ACCEPT_PORT', (ports,)) + public.write_log_gettext("Firewall manager", 'Successfully accepted port [{}]!', (ports,)) addtime = time.strftime('%Y-%m-%d %X', time.localtime()) result = public.M('firewall').add('protocol,types,port,address_ip,ps,addtime', (protocol,types,ports,address_ip,ps,addtime)) self.FirewallReload() - return public.returnMsg(True, 'ADD_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') # 删除指定放行端口 def DelSpecifiesIp(self, get): @@ -470,7 +470,7 @@ class firewalls: address_ip=get.address protocol_list = ['tcp', 'udp'] id = get.id - if protocol not in protocol_list: return public.returnMsg(False, 'DESIGNATED_POROTOCOL_NOT_EXIST') + if protocol not in protocol_list: return public.return_msg_gettext(False, 'Specified protocol does NOT exist!') if self.__isUfw: if type=='accept': public.ExecShell('ufw delete allow proto ' + protocol + ' from ' + address_ip + ' to any port ' + ports + '') @@ -485,10 +485,10 @@ class firewalls: public.ExecShell('iptables -D INPUT -s ' + address_ip + ' -p ' + protocol + ' --dport ' + ports + ' -j ACCEPT') else: public.ExecShell('iptables -D INPUT -s ' + address_ip + ' -p ' + protocol + ' --dport ' + ports + ' -j DROP') - public.WriteLog("TYPE_FIREWALL", 'FIREWALL_DROP_PORT', (ports,)) + public.write_log_gettext("Firewall manager", 'FIREWALL_DROP_PORT', (ports,)) public.M('firewall').where("id=?", (id,)).delete() self.FirewallReload() - return public.returnMsg(True, 'DEL_SUCCESS') + return public.return_msg_gettext(True, 'Successfully deleted') diff --git a/class/firewalld.py b/class/firewalld.py index 4ae41a6b..125cf45a 100644 --- a/class/firewalld.py +++ b/class/firewalld.py @@ -321,7 +321,7 @@ class firewalld: # 服务控制 def FirewalldService(self, type): public.ExecShell('systemctl ' + type + ' firewalld.service') - return public.returnMsg(True, 'SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') # 保存配置 def Save(self): diff --git a/class/firewalls.py b/class/firewalls.py index 14a0905e..1c436111 100644 --- a/class/firewalls.py +++ b/class/firewalls.py @@ -11,10 +11,13 @@ class firewalls: __isFirewalld = False __isUfw = False __Obj = None + __ufw_exec = 'ufw' def __init__(self): if os.path.exists('/usr/sbin/firewalld'): self.__isFirewalld = True - if os.path.exists('/usr/sbin/ufw'): self.__isUfw = True + if os.path.exists('/usr/sbin/ufw'): + self.__ufw_exec = '/usr/sbin/ufw' + self.__isUfw = True if self.__isFirewalld: try: self.__Obj = firewalld.firewalld() @@ -53,38 +56,76 @@ class firewalls: #重载防火墙配置 def FirewallReload(self): if self.__isUfw: - public.ExecShell('/usr/sbin/ufw reload') + public.ExecShell('{} reload &'.format(self.__ufw_exec)) return if self.__isFirewalld: public.ExecShell('firewall-cmd --reload') else: - public.ExecShell('/etc/init.d/iptables save') - public.ExecShell('/etc/init.d/iptables restart') + public.ExecShell('/etc/init.d/iptables save &') + public.ExecShell('/etc/init.d/iptables restart &') #取防火墙状态 def CheckFirewallStatus(self): - if self.__isUfw: - return 1 + # if self.__isUfw: + # res = public.ExecShell('ufw status verbose')[0] + # if res.find('inactive') != -1: return False + # return True + # if self.__isFirewalld: + # res = public.ExecShell("systemctl status firewalld")[0] + # if res.find('active (running)') != -1: return True + # if res.find('disabled') != -1: return False + # if res.find('inactive (dead)') != -1: return False + # else: + # res = public.ExecShell("/etc/init.d/iptables status")[0] + # if res.find('not running') != -1: return False + # return True + return public.get_firewall_status() == 1 + + def SetFirewallStatus(self,get=None): + ''' + @name 设置系统防火墙状态 + @author hwliang<2022-01-13> + ''' + status = not self.CheckFirewallStatus() + status_msg = {False: 'Close', True: 'Open'} + if self.__isUfw: + if status: + public.ExecShell('echo y|{} enable'.format(self.__ufw_exec)) + else: + public.ExecShell('echo y|{} disable'.format(self.__ufw_exec)) if self.__isFirewalld: - res = public.ExecShell("systemctl status firewalld")[0] - if res.find('active (running)') != -1: return 1 - if res.find('disabled') != -1: return -1 - if res.find('inactive (dead)') != -1: return 0 + if status: + public.ExecShell('systemctl enable firewalld') + public.ExecShell('systemctl start firewalld') + else: + public.ExecShell('systemctl disable firewalld') + public.ExecShell('systemctl stop firewalld') else: - return 1 + if status: + public.ExecShell("chkconfig iptables on") + public.ExecShell('/etc/init.d/iptables start') + else: + public.ExecShell("chkconfig iptables off") + public.ExecShell('/etc/init.d/iptables stop') + public.write_log_gettext('Firewall manager','{} system firewall!',(status_msg[status],)) + return public.return_msg_gettext(True,'{} system firewall!',(status_msg[status],)) #添加屏蔽IP def AddDropAddress(self,get): + if not self.CheckFirewallStatus(): return public.return_msg_gettext(False,'The system firewall is not open') import time import re ip_format = get.port.split('/')[0] - if not public.check_ip(ip_format): return public.returnMsg(False,'FIREWALL_IP_FORMAT') - if ip_format in ['0.0.0.0','127.0.0.0',"::1"]: return public.returnMsg(False,'请不要花样作死!') + if not public.check_ip(ip_format): return public.return_msg_gettext(False,'IP address you entered is illegal!') + if ip_format in ['0.0.0.0','127.0.0.0',"::1"]: return public.return_msg_gettext(False,'Disabling this IP will cause your server to fail') address = get.port - if public.M('firewall').where("port=?",(address,)).count() > 0: return public.returnMsg(False,'FIREWALL_IP_EXISTS') + if public.M('firewall').where("port=?",(address,)).count() > 0: return public.return_msg_gettext(False,'The IP exists in block list, no need to repeat processing!') if self.__isUfw: - public.ExecShell('ufw insert 1 deny from ' + address + ' to any') + if public.is_ipv6(ip_format): + public.ExecShell('{} deny from '.format(self.__ufw_exec) + address + ' to any') + else: + public.ExecShell('{} insert 1 deny from '.format(self.__ufw_exec) + address + ' to any') else: if self.__isFirewalld: #self.__Obj.AddDropAddress(address) @@ -93,26 +134,26 @@ class firewalls: else: public.ExecShell('firewall-cmd --permanent --add-rich-rule=\'rule family=ipv4 source address="'+ address +'" drop\'') else: - if public.is_ipv6(ip_format): return public.returnMsg(False,'FIREWALL_IP_FORMAT') + if public.is_ipv6(ip_format): return public.return_msg_gettext(False,'IP address is illegal!') public.ExecShell('iptables -I INPUT -s '+address+' -j DROP') public.WriteLog("TYPE_FIREWALL", 'FIREWALL_DROP_IP',(address,)) addtime = time.strftime('%Y-%m-%d %X',time.localtime()) public.M('firewall').add('port,ps,addtime',(address,get.ps,addtime)) self.FirewallReload() - return public.returnMsg(True,'ADD_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #删除IP屏蔽 def DelDropAddress(self,get): + if not self.CheckFirewallStatus(): return public.return_msg_gettext(False,'The system firewall is not open') address = get.port id = get.id ip_format = get.port.split('/')[0] if self.__isUfw: - public.ExecShell('ufw delete deny from ' + address + ' to any') + public.ExecShell('{} delete deny from '.format(self.__ufw_exec) + address + ' to any') else: if self.__isFirewalld: - #self.__Obj.DelDropAddress(address) if public.is_ipv6(ip_format): public.ExecShell('firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv6 source address="'+ address +'" drop\'') else: @@ -124,73 +165,81 @@ class firewalls: public.M('firewall').where("id=?",(id,)).delete() self.FirewallReload() - return public.returnMsg(True,'DEL_SUCCESS') + return public.return_msg_gettext(True,'Successfully deleted') #添加放行端口 def AddAcceptPort(self,get): + if not self.CheckFirewallStatus(): return public.return_msg_gettext(False,'The system firewall is not open') import re src_port = get.port get.port = get.port.replace('-',':') rep = r"^\d{1,5}(:\d{1,5})?$" if not re.search(rep,get.port): - return public.returnMsg(False,'PORT_CHECK_RANGE') + return public.return_msg_gettext(False,'Port range must be between 22 and 65535!') import time port = get.port - ps = public.xssencode(get.ps) + ps = "" + if get.ps: + ps = public.xssencode2(get.ps) is_exists = public.M('firewall').where("port=? or port=?",(port,src_port)).count() - if is_exists: return public.returnMsg(False,'FIREWALL_PORT_EXISTS') - notudps = ['80','443','8888','888','39000:40000','21','22','7800'] + if is_exists: return public.return_msg_gettext(False,'The port exists, no need to repeat the release!') + notudps = ['80','443','8888','888','39000:40000','21','22'] if self.__isUfw: - public.ExecShell('ufw allow ' + port + '/tcp') - if not port in notudps: public.ExecShell('ufw allow ' + port + '/udp') + public.ExecShell('{} allow '.format(self.__ufw_exec) + port + '/tcp') + # if not port in notudps: public.ExecShell('ufw allow ' + port + '/udp') else: if self.__isFirewalld: #self.__Obj.AddAcceptPort(port) port = port.replace(':','-') public.ExecShell('firewall-cmd --permanent --zone=public --add-port='+port+'/tcp') - if not port in notudps: public.ExecShell('firewall-cmd --permanent --zone=public --add-port='+port+'/udp') + # if not port in notudps: public.ExecShell('firewall-cmd --permanent --zone=public --add-port='+port+'/udp') else: public.ExecShell('iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport '+port+' -j ACCEPT') - if not port in notudps: public.ExecShell('iptables -I INPUT -p tcp -m state --state NEW -m udp --dport '+port+' -j ACCEPT') + # if not port in notudps: public.ExecShell('iptables -I INPUT -p tcp -m state --state NEW -m udp --dport '+port+' -j ACCEPT') public.WriteLog("TYPE_FIREWALL", 'FIREWALL_ACCEPT_PORT',(port,)) addtime = time.strftime('%Y-%m-%d %X',time.localtime()) if not is_exists: public.M('firewall').add('port,ps,addtime',(port,ps,addtime)) self.FirewallReload() - return public.returnMsg(True,'ADD_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #添加放行端口 def AddAcceptPortAll(self,port,ps): + if not self.CheckFirewallStatus(): return public.return_msg_gettext(False,'The system firewall is not open') import re port = port.replace('-',':') rep = r"^\d{1,5}(:\d{1,5})?$" if not re.search(rep,port): return False if self.__isUfw: - public.ExecShell('ufw allow ' + port + '/tcp') - public.ExecShell('ufw allow ' + port + '/udp') + public.ExecShell('{} allow '.format(self.__ufw_exec) + port + '/tcp') + # public.ExecShell('ufw allow ' + port + '/udp') else: if self.__isFirewalld: port = port.replace(':','-') public.ExecShell('firewall-cmd --permanent --zone=public --add-port='+port+'/tcp') - public.ExecShell('firewall-cmd --permanent --zone=public --add-port='+port+'/udp') + # public.ExecShell('firewall-cmd --permanent --zone=public --add-port='+port+'/udp') else: public.ExecShell('iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport '+port+' -j ACCEPT') - public.ExecShell('iptables -I INPUT -p tcp -m state --state NEW -m udp --dport '+port+' -j ACCEPT') + # public.ExecShell('iptables -I INPUT -p tcp -m state --state NEW -m udp --dport '+port+' -j ACCEPT') return True - + #删除放行端口 def DelAcceptPort(self,get): + if not self.CheckFirewallStatus(): return public.return_msg_gettext(False,'The system firewall is not open') port = get.port id = get.id + + if public.is_ipv6(str(port)): return self.DelDropAddress(get) # 如果是ipv6地址,则调用DelDropAddress + try: if(port == public.GetHost(True) or port == public.readFile('data/port.pl').strip()): - return public.returnMsg(False,'FIREWALL_PORT_PANEL') + return public.return_msg_gettext(False,'Failed,cannot delete current port of the panel') if self.__isUfw: - public.ExecShell('ufw delete allow ' + port + '/tcp') - public.ExecShell('ufw delete allow ' + port + '/udp') + public.ExecShell('{} delete allow '.format(self.__ufw_exec) + port + '/tcp') + public.ExecShell('{} delete allow '.format(self.__ufw_exec) + port + '/udp') else: if self.__isFirewalld: #self.__Obj.DelAcceptPort(port) @@ -201,35 +250,42 @@ class firewalls: public.ExecShell('iptables -D INPUT -p tcp -m state --state NEW -m udp --dport '+port+' -j ACCEPT') public.WriteLog("TYPE_FIREWALL", 'FIREWALL_DROP_PORT',(port,)) public.M('firewall').where("id=?",(id,)).delete() - + self.FirewallReload() - return public.returnMsg(True,'DEL_SUCCESS') + return public.return_msg_gettext(True,'Successfully deleted') except: - return public.returnMsg(False,'DEL_ERROR') + return public.return_msg_gettext(False,'Failed to delete') #设置远程端口状态 def SetSshStatus(self,get): - version = public.readFile('/etc/redhat-release') + # version = public.readFile('/etc/redhat-release') if int(get['status'])==1: - msg = public.getMsg('FIREWALL_SSH_STOP') + msg = public.get_msg_gettext('SSH service turned off') act = 'stop' else: - msg = public.getMsg('FIREWALL_SSH_START') + msg = public.get_msg_gettext('SSH service turned on') act = 'start' - - if not os.path.exists('/etc/redhat-release'): - public.ExecShell('service ssh ' + act) - elif version.find(' 7.') != -1 or version.find(' 8.') != -1 or version.find('Fedora') != -1: - public.ExecShell("systemctl "+act+" sshd.service") - else: - public.ExecShell("/etc/init.d/sshd "+act) - - public.WriteLog("TYPE_FIREWALL", msg) - return public.returnMsg(True,'SUCCESS') - - - + # if not os.path.exists('/etc/redhat-release'): + # public.ExecShell('service ssh ' + act) + # elif version.find(' 7.') != -1 or version.find(' 8.') != -1 or version.find('Fedora') != -1: + # public.ExecShell("systemctl "+act+" sshd") + # else: + # 全试一次? + public.ExecShell("/etc/init.d/sshd "+act) + public.ExecShell('service ssh ' + act) + public.ExecShell("systemctl "+act+" sshd") + public.ExecShell("systemctl "+act+" ssh") + if act in ['start'] and not public.get_sshd_status(): + msg = 'Service SSHD start failed!' + public.WriteLog("TYPE_FIREWALL", msg) + return public.returnMsg(False,msg) + public.WriteLog("TYPE_FIREWALL", msg) + return public.return_msg_gettext(True,'Setup successfully!') + + + + #设置ping def SetPing(self,get): if get.status == '1': @@ -243,20 +299,27 @@ class firewalls: conf = re.sub(rep,'net.ipv4.icmp_echo_ignore_all='+get.status,conf) else: conf += "\nnet.ipv4.icmp_echo_ignore_all="+get.status - - - public.writeFile(filename,conf) - public.ExecShell('sysctl -p') - return public.returnMsg(True,'SUCCESS') - - + + + if public.writeFile(filename,conf): + public.ExecShell('sysctl -p') + return public.return_msg_gettext(True,'Setup successfully!') + else: + return public.returnMsg(False,'Setup failed!') + + #改远程端口 def SetSshPort(self,get): port = get.port - if int(port) < 22 or int(port) > 65535: return public.returnMsg(False,'FIREWALL_SSH_PORT_ERR') + if not port: + return public.return_msg_gettext(False, 'Port range must be between 22 and 65535!') + try: + if int(port) < 22 or int(port) > 65535: return public.return_msg_gettext(False,'Port range must be between 22 and 65535!') + except: + return public.return_msg_gettext(False, 'Port range must be between 22 and 65535!') ports = ['21','25','80','443','8080','888','8888','7800'] - if port in ports: return public.returnMsg(False,'DONT_USE_PORT') + if port in ports: return public.return_msg_gettext(False,'Do NOT use common default port!') file = '/etc/ssh/sshd_config' conf = public.readFile(file) @@ -270,47 +333,22 @@ class firewalls: public.ExecShell('sed -i "s#SELINUX=enforcing#SELINUX=disabled#" /etc/selinux/config') public.ExecShell("systemctl restart sshd.service") elif self.__isUfw: - public.ExecShell('ufw allow ' + port + '/tcp') + public.ExecShell('{} allow '.format(self.__ufw_exec) + port + '/tcp') public.ExecShell("service ssh restart") else: public.ExecShell('iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport '+port+' -j ACCEPT') public.ExecShell("/etc/init.d/sshd restart") - + self.FirewallReload() public.M('firewall').where("ps=? or ps=? or port=?",('SSH remote management service','SSH remote service',port)).delete() public.M('firewall').add('port,ps,addtime',(port,'SSH remote service',time.strftime('%Y-%m-%d %X',time.localtime()))) public.WriteLog("TYPE_FIREWALL", "FIREWALL_SSH_PORT",(port,)) - return public.returnMsg(True,'EDIT_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #取SSH信息 def GetSshInfo(self,get): - port = public.get_ssh_port() - - pid_file = '/run/sshd.pid' - if os.path.exists(pid_file): - pid = int(public.readFile(pid_file)) - status = public.pid_exists(pid) - else: - import system - panelsys = system.system() - - version = panelsys.GetSystemVersion() - if os.path.exists('/usr/bin/apt-get'): - if os.path.exists('/etc/init.d/sshd'): - status = public.ExecShell("service sshd status | grep -P '(dead|stop)'|grep -v grep") - else: - status = public.ExecShell("service ssh status | grep -P '(dead|stop)'|grep -v grep") - else: - if version.find(' 7.') != -1 or version.find(' 8.') != -1 or version.find('Fedora') != -1: - status = public.ExecShell("systemctl status sshd.service | grep 'dead'|grep -v grep") - else: - status = public.ExecShell("/etc/init.d/sshd status | grep -e 'stopped' -e '已停'|grep -v grep") - - # return status; - if len(status[0]) > 3: - status = False - else: - status = True + port = public.get_sshd_port() + status = public.get_sshd_status() isPing = True try: file = '/etc/sysctl.conf' @@ -325,6 +363,6 @@ class firewalls: data['port'] = port data['status'] = status data['ping'] = isPing + data['firewall_status'] = self.CheckFirewallStatus() return data - - \ No newline at end of file + diff --git a/class/flask_compress.py b/class/flask_compress.py index 9e6adb5b..e000d6ca 100644 --- a/class/flask_compress.py +++ b/class/flask_compress.py @@ -2,7 +2,7 @@ import sys,os from gzip import GzipFile from io import BytesIO -from flask import request, current_app,session,Response,g +from flask import request, current_app,session,Response,g,abort if sys.version_info[:2] == (2, 6): @@ -80,7 +80,8 @@ class Compress(object): accept_encoding = request.headers.get('Accept-Encoding', '') response.headers['Server'] = 'nginx' response.headers['Connection'] = 'keep-alive' - + if not 'tmp_login' in session: + response.headers["X-Frame-Options"] = "SAMEORIGIN" if 'dologin' in g and app.config['SSL']: try: for k,v in request.cookies.items(): @@ -104,6 +105,23 @@ class Compress(object): if request_token: response.set_cookie('request_token',request_token,path='/',max_age=86400 * 30) + if response.content_length is not None: + if response.content_length < 512: + if not session.get('login',None) or g.get('api_request',None): + import public + default_pl = "{}/default.pl".format(public.get_panel_path()) + default_body = public.readFile(default_pl,'rb') + + if default_body: + if not default_body: default_body = b"" + resp_body = response.get_data() + + if default_body and resp_body.find(default_body.strip()) != -1: + result = b'{"status":false,"msg":"Error: 403 Forbidden"}' + response.set_data(result) + response.headers['Content-Length'] = len(result) + return response + if (response.mimetype not in app.config['COMPRESS_MIMETYPES'] or 'gzip' not in accept_encoding.lower() or diff --git a/class/flask_session/sessions.py b/class/flask_session/sessions.py index cc8737bb..60b8f93b 100644 --- a/class/flask_session/sessions.py +++ b/class/flask_session/sessions.py @@ -286,25 +286,21 @@ class MemcachedSessionInterface(SessionInterface): session_id = self._get_signer(app).sign(want_bytes(session.sid)) else: session_id = session.sid - from BTPanel import request, g, get_input + from BTPanel import request,g,get_input if 'auth_error' in g: return - if request.path in ['/', '/tips','/robots.txt']: return + if request.path in ['/', '/tips', '/robots.txt', '/favicon.ico', '/hook', '/close', '/down/']: return if request.path in ['/public']: get = get_input() - if 'get_ping' in get: return + if not 'get_ping' in get: return if response.status_code in [401]: return if request.full_path.find('/login?tmp_token=') != 0: - if response.status_code not in [200, 308]: return + if response.status_code not in [200,308]: return else: - if response.status_code not in [302, 301]: return + if response.status_code not in [302,301]: return if secure: samesite = 'None' - if response.status_code not in [200,302]: return if not request.cookies.get(app.session_cookie_name): - if request.full_path.find('/login?tmp_token=') == 0: - samesite = 'None' - secure = True response.set_cookie(app.session_cookie_name, session_id, expires=expires, httponly=httponly, domain=domain, path=path, secure=secure,samesite=samesite) diff --git a/class/ftp.py b/class/ftp.py index b12f1203..c7b51127 100644 --- a/class/ftp.py +++ b/class/ftp.py @@ -20,94 +20,100 @@ class ftp: #添加FTP def AddUser(self,get): try: - if not os.path.exists('/www/server/pure-ftpd/sbin/pure-ftpd'): return public.returnMsg(False,'Please install the Pure-FTPd service in the software store first.') + if not os.path.exists('/www/server/pure-ftpd/sbin/pure-ftpd'): return public.return_msg_gettext(False,'Please install the Pure-FTPd service in the software store first.') import files,time fileObj=files.files() - if re.search("\W + ",get['ftp_username']): return {'status':False,'code':501,'msg':public.getMsg('FTP_USERNAME_ERR_T')} - if len(get['ftp_username']) < 3: return {'status':False,'code':501,'msg':public.getMsg('FTP_USERNAME_ERR_LEN')} - if not fileObj.CheckDir(get['path']): return {'status':False,'code':501,'msg':public.getMsg('FTP_USERNAME_ERR_DIR')} - if public.M('ftps').where('name=?',(get.ftp_username.strip(),)).count(): return public.returnMsg(False,'FTP_USERNAME_ERR_EXISTS',(get.ftp_username,)) - username = get['ftp_username'].replace(' ','') - password = get['ftp_password'] + if get['ftp_username'].strip().find(' ') != -1: return public.returnMsg(False,'Username cannot contain spaces') + if re.search("\W + ",get['ftp_username']): return {'status':False,'code':501,'msg':public.get_msg_gettext('Username is illegal, special characters are NOT allowed!')} + if len(get['ftp_username']) < 3: return {'status':False,'code':501,'msg':public.get_msg_gettext('Username is illegal, cannot be less than 3 characters!')} + if not fileObj.CheckDir(get['path']): return {'status':False,'code':501,'msg':public.get_msg_gettext('System critical directory cannot be used as FTP directory!')} + if public.M('ftps').where('name=?',(get.ftp_username.strip(),)).count(): return public.return_msg_gettext(False,'User [{}] exists!',(get.ftp_username,)) + username = get['ftp_username'].strip() + if re.search("[\/\\\:\*\?\"\'\<\>\|]+",username): + return public.return_msg_gettext(False,"Name cannot contain /\:*?\"<>| symbol") + password = get['ftp_password'].strip() + if len(password) < 6: return public.return_msg_gettext(False, 'Password must be at least [{}] characters',("6",)) get.path = get['path'].replace(' ','') get.path = get.path.replace("\\", "/") fileObj.CreateDir(get) public.ExecShell('chown www.www ' + get.path) - public.ExecShell(self.__runPath + '/pure-pw useradd ' + username + ' -u www -d ' + get.path + '< {}',(username,str(ex))) + return public.return_msg_gettext(False,'Failed to add') #删除用户 def DeleteUser(self,get): try: username = get['username'] id = get['id'] - public.ExecShell(self.__runPath + '/pure-pw userdel ' + username) + public.ExecShell(self.__runPath + '/pure-pw userdel "' + username + '"') self.FtpReload() public.M('ftps').where("id=?",(id,)).delete() - public.WriteLog('TYPE_FTP', 'FTP_DEL_SUCCESS',(username,)) - return public.returnMsg(True, "DEL_SUCCESS") + public.write_log_gettext('FTP manager', 'Successfully deleted FTP user[{}]!',(username,)) + return public.return_msg_gettext(True, 'Successfully deleted') except Exception as ex: - public.WriteLog('TYPE_FTP', 'FTP_DEL_ERR',(username,str(ex))) - return public.returnMsg(False,'DEL_ERROR') + public.write_log_gettext('FTP manager', 'Faided to delete FTP user[{}]! => {}',(username,str(ex))) + return public.return_msg_gettext(False,'Failed to delete') #修改用户密码 def SetUserPassword(self,get): try: id = get['id'] - username = get['ftp_username'] - password = get['new_password'] - public.ExecShell(self.__runPath + '/pure-pw passwd ' + username + '< {}',(username,str(ex))) + return public.return_msg_gettext(False,'Failed to modify') #设置用户状态 def SetStatus(self,get): - msg = public.getMsg('OFF'); - if get.status != '0': msg = public.getMsg('ON'); + msg = public.get_msg_gettext('Turn off'); + if get.status != '0': msg = public.get_msg_gettext('Turn on'); try: id = get['id'] username = get['username'] status = get['status'] if int(status)==0: - public.ExecShell(self.__runPath + '/pure-pw usermod ' + username + ' -r 1') + public.ExecShell(self.__runPath + '/pure-pw usermod "' + username + '" -r 1') else: - public.ExecShell(self.__runPath + '/pure-pw usermod ' + username + " -r ''") + public.ExecShell(self.__runPath + '/pure-pw usermod "' + username + "\" -r ''") self.FtpReload() public.M('ftps').where("id=?",(id,)).setField('status',status) - public.WriteLog('TYPE_FTP','FTP_STATUS', (msg,username)) - return public.returnMsg(True, 'SUCCESS') + public.write_log_gettext('FTP manager','Successfully {} FTP user [{}]!', (msg,username)) + return public.return_msg_gettext(True, 'Setup successfully!') except Exception as ex: - public.WriteLog('TYPE_FTP','FTP_STATUS_ERR', (msg,username,str(ex))) - return public.returnMsg(False,'FTP_STATUS_ERR',(msg,)) + public.write_log_gettext('FTP manager','Failed to {} FTP user [{}]! => {}', (msg,username,str(ex))) + return public.return_msg_gettext(False,'{} FTP user failed!',(msg,)) ''' * 设置FTP端口 - * @param Int _GET['port'] 端口号 + * @param Int _GET['port'] 端口号 * @return bool ''' def setPort(self,get): try: - port = get['port'] - if int(port) < 1 or int(port) > 65535: return public.returnMsg(False,'PORT_CHECK_RANGE') + port = get['port'].strip() + if not port: return public.returnMsg(False,'Please enter an integer for the port') + if int(port) < 1 or int(port) > 65535: return public.return_msg_gettext(False,'Port range is incorrect!') file = '/www/server/pure-ftpd/etc/pure-ftpd.conf' conf = public.readFile(file) rep = u"\n#?\s*Bind\s+[0-9]+\.[0-9]+\.[0-9]+\.+[0-9]+,([0-9]+)" @@ -115,18 +121,66 @@ class ftp: conf = re.sub(rep,"\nBind 0.0.0.0," + port,conf) public.writeFile(file,conf) public.ExecShell('/etc/init.d/pure-ftpd restart') - public.WriteLog('TYPE_FTP', "FTP_PORT",(port,)) + public.write_log_gettext('FTP manager', "Successfully modified FTP port to [{}]!",(port,)) #添加防火墙 #data = ftpinfo(port=port,ps = 'FTP端口') get.port=port - get.ps = public.getMsg('FTP_PORT_PS'); + get.ps = public.get_msg_gettext('FTP port'); firewalls.firewalls().AddAcceptPort(get) session['port']=port - return public.returnMsg(True, 'EDIT_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') except Exception as ex: - public.WriteLog('TYPE_FTP', 'FTP_PORT_ERR',(str(ex),)) - return public.returnMsg(False,'EDIT_ERROR') + public.write_log_gettext('FTP manager', 'Failed to modify FTP port! => {}',(str(ex),)) + return public.return_msg_gettext(False,'Failed to modify') #重载配置 def FtpReload(self): public.ExecShell(self.__runPath + '/pure-pw mkdb /www/server/pure-ftpd/etc/pureftpd.pdb') + + #修改用户密码 + def set_user_home(self,get): + """ + change user home + id: ftp id + path: the new ftp user home + ftp_username: ftp username + migrate: migrate ftp user data to the new home + + """ + try: + id = get['id'] + path = get['path'] + username = get['ftp_username'] + # get the old path in the panel sqlite db + old_path = public.M("ftps").where("id=?",(id,)).getField('path') + # check the auth ftp user if exists + auth_conf_file = '/www/server/pure-ftpd/etc/pureftpd.passwd' + auth_conf = public.readFile(auth_conf_file) + if not auth_conf: + return public.returnMsg(False,'FTP account has not been set up') + # get the user specified conf + auth_conf_list = [i for i in auth_conf.split('\n')] + rep = '^{}:.*'.format(username) + macth_conf = [i for i in auth_conf_list if re.search(rep,i)] + if not macth_conf: + return public.returnMsg(False, 'FTP account has not been set up1') + if len(macth_conf) > 1: + return public.returnMsg(False, 'Matching multiple configurations, this operation has been stopped!') + if not os.path.exists(path): + os.makedirs(path) + public.ExecShell('chown www.www ' + path) + # replace the old path + result = macth_conf[0] + specified_user_conf = result.replace(old_path,path) + auth_conf = auth_conf.replace(result,specified_user_conf) + public.writeFile(auth_conf_file,auth_conf) + if get.migrate == '1': + public.ExecShell('cp -rp {}/* {}'.format(old_path,path)) + self.FtpReload() + public.M('ftps').where("id=?",(id,)).setField('path',path) + public.write_log_gettext('FTP manager', 'Successfully changed password for FTP user[{}]!',(path,)) + return public.return_msg_gettext(True,'Setup successfully!') + except Exception as ex: + return public.get_error_info() + public.write_log_gettext('FTP manager', 'FTP_PASS_ERR',(path,str(ex))) + return public.returnMsg(False,'EDIT_ERROR') \ No newline at end of file diff --git a/class/http_requests.py b/class/http_requests.py index e7f4977d..9d7b4c2d 100644 --- a/class/http_requests.py +++ b/class/http_requests.py @@ -14,22 +14,31 @@ import os,sys,re import ssl import public import json +import socket +import requests +import requests.packages.urllib3.util.connection as urllib3_conn +from requests.packages.urllib3.exceptions import InsecureRequestWarning +requests.packages.urllib3.disable_warnings(InsecureRequestWarning) class http: - def __init__(self): - pass - def get(self,url,timeout = 60,headers = {},verify = False,type = 'python'): url = self.quote(url) if type == 'python': + old_family = urllib3_conn.allowed_gai_family try: - import requests - from requests.packages.urllib3.exceptions import InsecureRequestWarning - requests.packages.urllib3.disable_warnings(InsecureRequestWarning) - from requests import get as req_get - return req_get(url,timeout=timeout,headers=get_headers(headers),verify=verify) + # 默认使用IPv4 + urllib3_conn.allowed_gai_family = lambda: socket.AF_INET + return requests.get(url,timeout=timeout,headers=get_headers(headers),verify=verify) except: - result = self._get_curl(url,timeout,headers,verify) + try: + # IPV6? + urllib3_conn.allowed_gai_family = lambda: socket.AF_INET6 + return requests.get(url,timeout=timeout,headers=get_headers(headers),verify=verify) + except: + # 使用CURL + result = self._get_curl(url,timeout,headers,verify) + urllib3_conn.allowed_gai_family = old_family + elif type == 'curl': result = self._get_curl(url,timeout,headers,verify) elif type == 'php': @@ -44,14 +53,20 @@ class http: def post(self,url,data,timeout = 60,headers = {},verify = False,type = 'python'): url = self.quote(url) if type == 'python': + old_family = urllib3_conn.allowed_gai_family try: - import requests - from requests.packages.urllib3.exceptions import InsecureRequestWarning - requests.packages.urllib3.disable_warnings(InsecureRequestWarning) - from requests import post as req_post - return req_post(url,data,timeout=timeout,headers=headers,verify=verify) + urllib3_conn.allowed_gai_family = lambda: socket.AF_INET + return requests.post(url,data,timeout=timeout,headers=headers,verify=verify) except: - result = self._post_curl(url,data,timeout,headers,verify) + try: + # IPV6? + urllib3_conn.allowed_gai_family = lambda: socket.AF_INET6 + return requests.post(url,data,timeout=timeout,headers=headers,verify=verify) + except: + # 使用CURL + result = self._post_curl(url,data,timeout,headers,verify) + urllib3_conn.allowed_gai_family = old_family + elif type == 'curl': result = self._post_curl(url,data,timeout,headers,verify) elif type == 'php': @@ -63,6 +78,30 @@ class http: result = self._post_py3(url,data,timeout,headers,verify) return result + + def download_file(self,url,filename,data = None,timeout = 1800,speed_file='/dev/shm/download_speed.pl'): + ''' + @name 下载文件 + @author hwliang<2021-07-08> + @param url 下载地址 + @param filename 保存路径 + @param data POST参数,不传则使用GET方法,否则使用POST方法 + @param timeout 超时时间,默认1800秒 + @param speed_file + ''' + import requests + from requests.packages.urllib3.exceptions import InsecureRequestWarning + requests.packages.urllib3.disable_warnings(InsecureRequestWarning) + headers = public.get_requests_headers() + if data is None: + res = requests.get(url,headers=headers,timeout=timeout,stream=True) + else: + res = requests.post(url,data,headers=headers,timeout=timeout,stream=True) + with open(filename,"wb") as f: + for _chunk in res.iter_content(chunk_size=8192): + f.write(_chunk) + + #POST请求 Python2 def _post_py2(self,url,data,timeout,headers,verify): import urllib2 @@ -114,18 +153,21 @@ class http: raise Exception('No PHP version available!') tmp_file = '/dev/shm/http.php' http_php = '''&1".format(self._curl_bin() + _ssl_verify,timeout,headers_str,url))[0] + result = public.ExecShell("{} -sS -i --connect-timeout {} {} {} 2>&1".format(self._curl_bin() + ' ' + str(_ssl_verify),timeout,headers_str,url))[0] r_body,r_headers,r_status_code = self._curl_format(result) return response(r_body,r_status_code,r_headers) @@ -220,11 +265,13 @@ exit($header."\r\n\r\n".json_encode($body)); raise Exception('No PHP version available!') tmp_file = '/dev/shm/http.php' http_php = '''''' public.writeFile(tmp_file,http_php) data = json.dumps({"url":url,"timeout":timeout,"verify":verify,"headers":self._php_headers(headers)}) + if php_version in ['53']: + php_version = '/www/server/php/' + php_version + '/bin/php' if php_version.find('/www/server/php') != -1: result = public.ExecShell(php_version + ' ' + tmp_file + " --post='" + data + "'" )[0] else: result = public.request_php(php_version,'/http.php','/dev/shm','POST',{"data":data}) + if isinstance(result,bytes): result = result.decode('utf-8') + if os.path.exists(tmp_file): os.remove(tmp_file) r_body,r_headers,r_status_code = self._curl_format(result) return response(json.loads(r_body).strip(),r_status_code,r_headers) @@ -259,7 +310,8 @@ exit($header."\r\n\r\n".json_encode($body)); #取可用的PHP版本 def _get_php_version(self): - php_versions = ['52','53','54','55','56','70','71','72','73','74','80'] + php_versions = public.get_php_versions() + php_versions = sorted(php_versions,reverse=True) php_path = '/www/server/php/{}/sbin/php-fpm' php_sock = '/tmp/php-cgi-{}.sock' for pv in php_versions: @@ -276,9 +328,11 @@ exit($header."\r\n\r\n".json_encode($body)); #取CURL路径 def _curl_bin(self): c_bin = ['/usr/local/curl2/bin/curl','/usr/local/curl/bin/curl','/usr/local/bin/curl','/usr/bin/curl'] + curl_bin = 'curl' for cb in c_bin: + if os.path.exists(cb): curl_bin = cb if os.path.exists(cb): return cb - return 'curl' + return curl_bin #格式化CURL响应头 def _curl_format(self,req): @@ -396,7 +450,7 @@ class response: return self.text DEFAULT_HEADERS = {"Content-type":"application/x-www-form-urlencoded","User-Agent":"BT-Panel"} -s_types = ['python','php','curl'] +s_types = ['python','php','curl','src'] DEFAULT_TYPE = 'python' __version__ = 1.0 diff --git a/class/jobs.py b/class/jobs.py index 8c70d124..b446df10 100644 --- a/class/jobs.py +++ b/class/jobs.py @@ -48,6 +48,19 @@ def control_init(): if not public.M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'sites','%project_config%')).count(): public.M('sites').execute("alter TABLE sites add project_config STRING DEFAULT '{}'",()) + if not public.M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'backup','%ps%')).count(): + public.M('backup').execute("alter TABLE backup add ps STRING DEFAULT 'No'",()) + + if not public.M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'databases','%db_type%')).count(): + public.M('databases').execute("alter TABLE databases add db_type integer DEFAULT '0'",()) + + if not public.M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'databases','%conn_config%')).count(): + public.M('databases').execute("alter TABLE databases add conn_config STRING DEFAULT '{}'",()) + + if not public.M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'databases','%sid%')).count(): + public.M('databases').execute("alter TABLE databases add sid integer DEFAULT 0",()) + + sql = db.Sql() if not sql.table('sqlite_master').where('type=? AND name=?', ('table', 'site_types')).count(): csql = '''CREATE TABLE IF NOT EXISTS `site_types` ( @@ -94,6 +107,18 @@ def control_init(): `logout_time` INTEGER, `expire` INTEGER, `addtime` INTEGER +)''' + sql.execute(csql,()) + + if not sql.table('sqlite_master').where('type=? AND name=?', ('table', 'database_servers')).count(): + csql = '''CREATE TABLE IF NOT EXISTS `database_servers` ( +`id` INTEGER PRIMARY KEY AUTOINCREMENT, +`db_host` REAL, +`db_port` REAL, +`db_user` INTEGER, +`db_password` INTEGER, +`ps` REAL, +`addtime` INTEGER )''' sql.execute(csql,()) @@ -161,10 +186,19 @@ def control_init(): public.ExecShell("rm -rf /www/server/panel/adminer") if os.path.exists('/dev/shm/session.db'): os.remove('/dev/shm/session.db') + + node_service_bin = '/usr/bin/nodejs-service' + node_service_src = '/www/server/panel/script/nodejs-service.py' + if os.path.exists(node_service_src): public.ExecShell("chmod 700 " + node_service_src) + if not os.path.exists(node_service_bin): + if os.path.exists(node_service_src): + public.ExecShell("ln -sf {} {}".format(node_service_src,node_service_bin)) + #disable_putenv('putenv') #clean_session() #set_crond() test_ping() + set_wp_cache_dir() clean_max_log('/www/server/panel/plugin/rsync/lsyncd.log') clean_max_log('/var/log/rsyncd.log',1024*1024*10) clean_max_log('/root/.pm2/pm2.log',1024*1024*20) @@ -182,7 +216,12 @@ def control_init(): update_py37() run_script() set_php_cli_env() + check_enable_php() +def set_wp_cache_dir(): + import one_key_wp + one_key_wp.fast_cgi().set_nginx_conf() + public.ExecShell("/etc/init.d/nginx restart") def set_php_cli_env(): ''' @@ -209,7 +248,7 @@ def set_php_cli_env(): # 设置所有已安装的PHP版本环境变量和别名 - php_versions_list = ['52','53','54','55','56','70','71','72','73','74','80','81','82','83','84','90','91'] + php_versions_list = public.get_php_versions() for php_version in php_versions_list: php_ini = "{}/{}/etc/php.ini".format(php_path,php_version) php_cli_ini = "{}/{}/etc/php-cli.ini".format(php_path,php_version) @@ -244,6 +283,30 @@ def set_php_cli_env(): public.writeFile(bashrc,bashrc_body) +def check_enable_php(): + ''' + @name 检查nginx下的php配置文件 + ''' + php_versions = public.get_php_versions() + ngx_php_conf = public.get_setup_path() + '/nginx/conf/enable-php-00.conf' + public.writeFile(ngx_php_conf,'') + for php_v in php_versions: + ngx_php_conf = public.get_setup_path() + '/nginx/conf/enable-php-{}.conf'.format(php_v) + if os.path.exists(ngx_php_conf): continue + enable_conf = ''' + location ~ [^/]\.php(/|$) + {{ + try_files $uri =404; + fastcgi_pass unix:/tmp/php-cgi-{}.sock; + fastcgi_index index.php; + include fastcgi.conf; + include pathinfo.conf; + }} + '''.format(php_v) + public.writeFile(ngx_php_conf,enable_conf) + + + def write_run_script_log(_log,rn='\n'): _log_file = '/www/server/panel/logs/run_script.log' public.writeFile(_log_file,_log + rn,'a+') @@ -319,7 +382,6 @@ def files_set_mode(): ["/www/server/stop","","root",755,True], ["/www/server/redis","","redis",700,True], ["/www/server/redis/redis.conf","","redis",600,False], - ["/www/Recycle_bin","","root",600,True], ["/www/server/panel/class","","root",600,True], ["/www/server/panel/data","","root",600,True], ["/www/server/panel/plugin","","root",600,False], @@ -345,6 +407,10 @@ def files_set_mode(): ["/www/server/coll","","root",700,True] ] + recycle_list = public.get_recycle_bin_list() + for recycle_path in recycle_list: + m_paths.append([recycle_path,'','root',600,True]) + for m in m_paths: if not os.path.exists(m[0]): continue path = m[0] + m[1] @@ -409,7 +475,7 @@ def set_pma_access(): #尝试升级到独立环境 def update_py37(): - pyenv='/www/server/panel/pyenv/bin/python' + pyenv='/www/server/panel/pyenv/bin/python3' pyenv_exists='/www/server/panel/data/pyenv_exists.pl' if os.path.exists(pyenv) or os.path.exists(pyenv_exists): return False download_url = public.get_url() @@ -552,7 +618,7 @@ def disable_putenv(fun_name): try: is_set_disable = '/www/server/panel/data/disable_%s' % fun_name if os.path.exists(is_set_disable): return True - php_vs = ('52','53','54','55','56','70','71','72','73','74') + php_vs = public.get_php_versions() php_ini = "/www/server/php/{0}/etc/php.ini" rep = "disable_functions\s*=\s*.*" for pv in php_vs: @@ -619,3 +685,8 @@ def clean_session(): except:return False + +if __name__ == '__main__': + control_init() + + diff --git a/class/monitor.py b/class/monitor.py index f777edee..84c1c484 100644 --- a/class/monitor.py +++ b/class/monitor.py @@ -12,6 +12,7 @@ import json import time import datetime import re +import sqlite3 import public @@ -40,6 +41,43 @@ class Monitor: return sites def _statuscode_distribute_site(self, site_name): + + try: + day_401 = 0 + day_500 = 0 + day_502 = 0 + day_503 = 0 + conn = None + ts = None + start_date, end_date = self.get_time_interval(time.localtime()) + select_sql = "select time/100 as time1, sum(status_401), sum(status_500), sum(status_502), sum(status_503) from request_stat where time between {} and {}"\ + .format(start_date, end_date) + + db_path = os.path.join("/www/server/total/", "logs/{}/logs.db".format(site_name)) + if os.path.isfile(db_path): + conn = sqlite3.connect(db_path) + ts = conn.cursor() + ts.execute(select_sql) + results = ts.fetchall() + + if type(results) == list: + for result in results: + time_key = str(result[0]) + day_401 = result[1] + day_500 = result[2] + day_502 = result[3] + day_503 = result[4] + except: + pass + finally: + if ts: + ts.close() + if conn: + conn.close() + + return day_401, day_500, day_502, day_503 + + def _statuscode_distribute_site_old(self, site_name): today = time.strftime('%Y-%m-%d', time.localtime()) path = '/www/server/total/total/' + site_name + '/request/' + today + '.json' @@ -52,10 +90,10 @@ class Monitor: for c in spdata.values(): for d in c: - if '401' == d: day_401 += c['401'] - if '500' == d: day_500 += c['500'] - if '502' == d: day_502 += c['502'] - if '503' == d: day_503 += c['503'] + if '401' == d: day_401 += c['401'] or 0 + if '500' == d: day_500 += c['500'] or 0 + if '502' == d: day_502 += c['502'] or 0 + if '503' == d: day_503 += c['503'] or 0 return day_401, day_500, day_502, day_503 @@ -66,6 +104,10 @@ class Monitor: for site in sites: site_name = site['name'] day_401, day_500, day_502, day_503 = self._statuscode_distribute_site(site_name) + day_401 = day_401 or 0 + day_500 = day_500 or 0 + day_502 = day_502 or 0 + day_503 = day_503 or 0 count_401 += day_401 count_500 += day_500 count_502 += day_502 @@ -177,8 +219,45 @@ class Monitor: data.update(statuscode_distribute) return data - # 获取蜘蛛数量分布 def get_spider(self, args): + request_data = {} + sites = public.M('sites').field('name').order("addtime").select(); + for site_info in sites: + ts = None + conn = None + try: + site_name = site_info["name"] + start_date, end_date = self.get_time_interval(time.localtime()) + select_sql = "select time, spider from request_stat where time between {} and {}"\ + .format(start_date, end_date) + + db_path = os.path.join("/www/server/total/", "logs/{}/logs.db".format(site_name)) + if not os.path.isfile(db_path): continue + conn = sqlite3.connect(db_path) + ts = conn.cursor() + ts.execute(select_sql) + results = ts.fetchall() + + if type(results) == list: + for result in results: + time_key = str(result[0]) + hour = time_key[len(time_key)-2:] + value = result[1] + if hour not in request_data: + request_data[hour] = value + else: + request_data[hour] += value + except: + pass + finally: + if ts: + ts.close() + if conn: + conn.close() + return request_data + + # 获取蜘蛛数量分布 + def get_spider_old(self, args): today = time.strftime('%Y-%m-%d', time.localtime()) sites = self._get_site_list() @@ -210,8 +289,53 @@ class Monitor: return {'load_five': load_five, 'cpu_count': cpu_count, 'up_flow': up_flow} - # 取每小时的请求数 + def get_time_interval(self, local_time): + start = None + end = None + time_key_format = "%Y%m%d00" + start = int(time.strftime(time_key_format, local_time)) + time_key_format = "%Y%m%d23" + end = int(time.strftime(time_key_format, local_time)) + return start, end + def get_request_count_by_hour(self, args): + # 获取站点每小时的请求数据 + request_data = {} + import sqlite3 + sites = public.M('sites').field('name').order("addtime").select(); + for site_info in sites: + ts = None + conn = None + try: + site_name = site_info["name"] + start_date, end_date = self.get_time_interval(time.localtime()) + select_sql = "select time, req from request_stat where time between {} and {}"\ + .format(start_date, end_date) + db_path = os.path.join("/www/server/total/", "logs/{}/logs.db".format(site_name)) + if not os.path.isfile(db_path): continue + conn = sqlite3.connect(db_path) + ts = conn.cursor() + ts.execute(select_sql) + results = ts.fetchall() + if type(results) == list: + for result in results: + time_key = str(result[0]) + hour = time_key[len(time_key)-2:] + value = result[1] + if hour not in request_data: + request_data[hour] = value + else: + request_data[hour] += value + except: pass + finally: + if ts: + ts.close() + if conn: + conn.close() + return request_data + + # 取每小时的请求数 + def get_request_count_by_hour_old(self, args): today = time.strftime('%Y-%m-%d', time.localtime()) request_data = {} diff --git a/class/nginx.py b/class/nginx.py index f5cf0122..44bfcf22 100644 --- a/class/nginx.py +++ b/class/nginx.py @@ -23,29 +23,29 @@ class nginx: proxycontent = public.readFile(self.proxyfile) for i in [[ngconfcontent,self.nginxconf],[proxycontent,self.proxyfile]]: if not i[0]: - return public.returnMsg(False,"Can not find nginx config file [ {} ]".format(i[1])) + return public.return_msg_gettext(False,"Can not find nginx config file [ {} ]".format(i[1])) unitrep = "[kmgKMG]" conflist = [] - ps = ["%s,%s" % (public.GetMsg("WORKER_PROCESSES"),public.GetMsg("WORKER_PROCESSES_AUTO")), - public.GetMsg("WORKER_CONNECTIONS"), - public.GetMsg("CONNECT_TIMEOUT_TIME"), - public.GetMsg("NGINX_ZIP"), - public.GetMsg("NGINX_ZIP_MIN"), - public.GetMsg("ZIP_COMP_LEVEL"), - public.GetMsg("UPLOAD_MAX_FILE"), - public.GetMsg("SERVER_NAME_HASH"), - public.GetMsg("CLIENT_HEADER_BUFF")] + ps = ["%s,%s" % (public.get_msg_gettext('Worker processes'),public.get_msg_gettext('Auto means automatic')), + public.get_msg_gettext('Worker connections'), + public.get_msg_gettext('Connection timeout'), + public.get_msg_gettext('Whether to enable compressed transmission'), + public.get_msg_gettext('Minimum file to compress'), + public.get_msg_gettext('Compression level'), + public.get_msg_gettext('Maximum file to upload'), + public.get_msg_gettext('Hash table size of server name'), + public.get_msg_gettext('Client header buffer size')] gets = ["worker_processes","worker_connections","keepalive_timeout","gzip","gzip_min_length","gzip_comp_level","client_max_body_size","server_names_hash_bucket_size","client_header_buffer_size"] n = 0 for i in gets: rep = "(%s)\s+(\w+)" % i k = re.search(rep, ngconfcontent) if not k: - return public.returnMsg(False,"Get key {} False".format(k)) + return public.return_msg_gettext(False,"Get key {} False".format(k)) k = k.group(1) v = re.search(rep, ngconfcontent) if not v: - return public.returnMsg(False,"Get value {} False".format(v)) + return public.return_msg_gettext(False,"Get value {} False".format(v)) v = v.group(2) if re.search(unitrep,v): u = str.upper(v[-1]) @@ -60,18 +60,18 @@ class nginx: kv = {"name":k,"value":v,"unit":u,"ps":psstr} conflist.append(kv) n += 1 - ps = [public.GetMsg("CLIENT_BODY_BUFF")] + ps = [public.get_msg_gettext('Client body buffer')] gets = ["client_body_buffer_size"] n = 0 for i in gets: rep = "(%s)\s+(\w+)" % i k = re.search(rep, proxycontent) if not k: - return public.returnMsg(False,"Get key {} False".format(k)) + return public.return_msg_gettext(False,"Get key {} False".format(k)) k=k.group(1) v = re.search(rep, proxycontent) if not v: - return public.returnMsg(False,"Get value {} False".format(v)) + return public.return_msg_gettext(False,"Get value {} False".format(v)) v = v.group(2) if re.search(unitrep, v): u = str.upper(v[-1]) @@ -86,7 +86,6 @@ class nginx: kv = {"name":k, "value":v, "unit":u,"ps":psstr} conflist.append(kv) n+=1 - print(conflist) return conflist def SetNginxValue(self,get): @@ -109,10 +108,10 @@ class nginx: rep = "%s\s+[^kKmMgG\;\n]+" % c["name"] if c["name"] == "worker_processes" or c["name"] == "gzip": if not re.search("auto|on|off|\d+", c["value"]): - return public.returnMsg(False, 'INIT_ARGS_ERR') + return public.return_msg_gettext(False, 'Parameter ERROR!') else: if not re.search("\d+", c["value"]): - return public.returnMsg(False, 'INIT_ARGS_ERR') + return public.return_msg_gettext(False, 'Parameter ERROR!') if re.search(rep,ngconfcontent): newconf = "%s %s" % (c["name"],c["value"]) ngconfcontent = re.sub(rep,newconf,ngconfcontent) @@ -125,10 +124,10 @@ class nginx: if (isError != True): shutil.copyfile('/tmp/ng_file_bk.conf', self.nginxconf) shutil.copyfile('/tmp/proxyfile_bk.conf', self.proxyfile) - return public.returnMsg(False, 'ERROR:
                                      ' + isError.replace("\n", + return public.return_msg_gettext(False, 'ERROR:
                                      ' + isError.replace("\n", '
                                      ') + '
                                      ') public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') def add_nginx_access_log_format(self,args): ''' @@ -151,14 +150,14 @@ class nginx: self.del_nginx_access_log_format(args) conf = public.readFile(self.nginxconf) if not conf: - return public.returnMsg(False,'NGINX_CONF_NOT_EXISTS') + return public.return_msg_gettext(False,'Nginx configuration file does not exist!') reg = 'http(\n|\s)+{' conf = re.sub(reg,'http\n\t{'+data,conf) public.writeFile(self.nginxconf,conf) public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') except: - return public.returnMsg(False, str(public.get_error_info())) + return public.return_msg_gettext(False, str(public.get_error_info())) def del_nginx_access_log_format(self,args): ''' @@ -169,13 +168,13 @@ class nginx: log_format_name = args.log_format_name conf = public.readFile(self.nginxconf) if not conf: - return public.returnMsg(False, 'NGINX_CONF_NOT_EXISTS') + return public.return_msg_gettext(False, 'Nginx configuration file does not exist!') reg = '\s*#LOG_FORMAT_BEGIN_{n}(\n|.)+#LOG_FORMAT_END_{n}\n?'.format(n=args.log_format_name) conf = re.sub(reg,'',conf) self._del_format_log_of_website(log_format_name) public.writeFile(self.nginxconf,conf) public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') def del_all_log_format(self,args): all_format = self.get_nginx_access_log_format(args) @@ -223,7 +222,7 @@ class nginx: reg = "#LOG_FORMAT_BEGIN.*" conf = public.readFile(self.nginxconf) if not conf: - return public.returnMsg(False, 'NGINX_CONF_NOT_EXISTS') + return public.return_msg_gettext(False, 'Nginx configuration file does not exist!') data = re.findall(reg,conf) format_name = [i.split('LOG_FORMAT_BEGIN_')[-1] for i in data] format_log = {} @@ -236,7 +235,7 @@ class nginx: format_log[i] = self._process_log_format(tmp) return format_log except: - return public.returnMsg(False,public.get_error_info()) + return public.return_msg_gettext(False,public.get_error_info()) def set_format_log_to_website(self,args): ''' @@ -254,7 +253,7 @@ class nginx: website_conf_file = '/www/server/panel/vhost/nginx/{}.conf'.format(site['name']) conf = public.readFile(website_conf_file) if not conf: - return public.returnMsg(False, 'NGINX_CONF_NOT_EXISTS') + return public.return_msg_gettext(False, 'Nginx configuration file does not exist!') format_exist_reg = '(access_log\s+/www.*\.log).*;' access_log = self.get_nginx_access_log(conf) if not access_log: @@ -267,9 +266,9 @@ class nginx: continue conf = re.sub(format_exist_reg,access_log,conf) public.writeFile(website_conf_file,conf) - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') except: - return public.returnMsg(False, str(public.get_error_info())) + return public.return_msg_gettext(False, str(public.get_error_info())) def get_nginx_access_log(self,nginx_conf): try: diff --git a/class/ols.py b/class/ols.py index b1184d9f..8fb22f7c 100644 --- a/class/ols.py +++ b/class/ols.py @@ -51,7 +51,7 @@ class ols: conf = conf + '\n{} {}'.format(k,data[k]) public.writeFile(self._main_conf_path,conf) public.serviceReload() - return public.returnMsg(True,"Setup Successfully") + return public.return_msg_gettext(True,"Setup Successfully!") # 获取站点静态文件缓存配置 def get_static_cache(self,get): @@ -101,7 +101,7 @@ class ols: conf = conf.replace(old_cache,new_cache) public.writeFile(self._detail_conf_path.format(sitename),conf) public.serviceReload() - return public.returnMsg(True,'Setup Successfully') + return public.return_msg_gettext(True,'Setup Successfully!') except Exception as e: return e @@ -180,14 +180,14 @@ RewriteRule (.*\.php)?$ - [E=Cache-Control:private] f.write(conf) f.close() public.serviceReload() - return public.returnMsg(True,'Open successfully') + return public.return_msg_gettext(True,'Setup successfully!') else: bt_conf_rep = '#.*BTLSCACHE_BEGIN(.|\n)+BTLSCACHE_END#*\n' conf = re.sub(bt_conf_rep,'',conf) print(conf) public.writeFile(file,conf) public.serviceReload() - return public.returnMsg(True,'Close successfully') + return public.return_msg_gettext(True,'Setup successfully!') def set_private_cache(self,get): """ @@ -215,7 +215,7 @@ RewriteRule (.*\.php)?$ - [E=Cache-Control:private] conf = re.sub(bt_conf_rep,bt_conf,conf) public.writeFile(file_name,conf) public.serviceReload() - return public.returnMsg(True,'Setup Successfully') + return public.return_msg_gettext(True,'Setup successfully!') def _get_site_domain(self): site = [] diff --git a/class/page.py b/class/page.py index ff7ee231..e8ebf7a3 100644 --- a/class/page.py +++ b/class/page.py @@ -12,14 +12,14 @@ class Page(): #-------------------------- # 分页类 - JS回调版 #-------------------------- - __PREV = public.GetMsg("PAGE")["PREV"] - __NEXT = public.GetMsg("PAGE")["NEXT"] - __START = public.GetMsg("PAGE")["START"] - __END = public.GetMsg("PAGE")["END"] - __COUNT_START = public.GetMsg("PAGE")["COUNT_START"] - __COUNT_END = public.GetMsg("PAGE")["COUNT_END"] - __FO = public.GetMsg("PAGE")["FO"] - __LINE = public.GetMsg("PAGE")["LINE"] + __PREV = public.get_msg_gettext('Prev') + __NEXT = public.get_msg_gettext('Next') + __START = public.get_msg_gettext('Start') + __END = public.get_msg_gettext('Last') + __COUNT_START = public.get_msg_gettext('Total') + __COUNT_END = '' + __FO = public.get_msg_gettext('From') + __LINE = '' __LIST_NUM = 4 SHIFT = None #偏移量 ROW = None #每页行数 @@ -30,18 +30,19 @@ class Page(): __RTURN_JS = False #是否返回JS回调 __START_NUM = None #起始行 __END_NUM = None #结束行 - + def __init__(self): - tmp = public.GetMsg('PAGE'); - if tmp: - self.__PREV = tmp['PREV']; - self.__NEXT = tmp['NEXT']; - self.__START = tmp['START']; - self.__END = tmp['END']; - self.__COUNT_START = tmp['COUNT_START']; - self.__COUNT_END = tmp['COUNT_END']; - self.__FO = tmp['FO']; - self.__LINE = tmp['LINE']; + pass + # tmp = public.get_msg_gettext('Depends on the following software, please install [{1}] first') + # if tmp: + # self.__PREV = tmp['Prev'] + # self.__NEXT = tmp['Next'] + # self.__START = tmp['Start'] + # self.__END = tmp['Last'] + # self.__COUNT_START = tmp['Total'] + # self.__COUNT_END = tmp[''] + # self.__FO = tmp['From'] + # self.__LINE = tmp[''] def GetPage(self,pageInfo,limit = '1,2,3,4,5,6,7,8'): # 取分页信息 @@ -56,9 +57,9 @@ class Page(): self.__COUNT_PAGE = self.__GetCountPage() self.__URI = self.__SetUri(pageInfo['uri']) self.SHIFT = self.__START_NUM - 1 - + keys = limit.split(',') - + pages = {} #起始页 pages['1'] = self.__GetStart() @@ -70,23 +71,22 @@ class Page(): pages['4'] = self.__GetNext() #尾页 pages['5'] = self.__GetEnd() - + #当前显示页与总页数 pages['6'] = "" + str(self.__C_PAGE) + "/" + str(self.__COUNT_PAGE) + "" #本页显示开始与结束行 pages['7'] = "" + self.__FO + str(self.__START_NUM) + "-" + str(self.__END_NUM) + self.__LINE + "" #行数 - pages['8'] = "" + self.__COUNT_START + str(self.__COUNT_ROW) + self.__COUNT_END + "" - + pages['8'] = "" + self.__COUNT_START +' '+ str(self.__COUNT_ROW) + self.__COUNT_END + "" #构造返回数据 retuls = '
                                      '; for value in keys: retuls += pages[value] retuls +='
                                      '; - + #返回分页数据 return retuls; - + def __GetEnd(self): #构造尾页 endStr = "" @@ -98,7 +98,7 @@ class Page(): else: endStr = "" + self.__END + "" return endStr - + def __GetNext(self): #构造下一页 nextStr = "" @@ -107,11 +107,11 @@ class Page(): else: if self.__RTURN_JS == "": nextStr = "" + self.__NEXT + "" - else: + else: nextStr = "" + self.__NEXT + "" - + return nextStr - + def __GetPages(self): #构造分页 pages = '' @@ -130,11 +130,11 @@ class Page(): pages += "" + str(page) + "" else: pages += "" + str(page) + "" - + #当前页 if self.__C_PAGE > 0: pages += "" + str(self.__C_PAGE) + "" - + #当前页之后 if self.__C_PAGE <= self.__LIST_NUM: num = self.__LIST_NUM + (self.__LIST_NUM - self.__C_PAGE) + 1 @@ -148,11 +148,11 @@ class Page(): break; if self.__RTURN_JS == "": pages += "" + str(page) + "" - else: + else: pages += "" + str(page) + "" - + return pages; - + def __GetPrev(self): #构造上一页 startStr = '' @@ -161,10 +161,10 @@ class Page(): else: if self.__RTURN_JS == "": startStr = "" + self.__PREV + "" - else: + else: startStr = "" + self.__PREV + "" return startStr - + def __GetStart(self): #构造起始分页 startStr = '' @@ -176,27 +176,27 @@ class Page(): else: startStr = "" + self.__START + "" return startStr; - + def __GetCpage(self,p): #取当前页 if p: return p return 1 - + def __StartRow(self): #从多少行开始 return (self.__C_PAGE - 1) * self.ROW + 1 - + def __EndRow(self): #从多少行结束 if self.ROW > self.__COUNT_ROW: return self.__COUNT_ROW return self.__C_PAGE * self.ROW - + def __GetCountPage(self): #取总页数 return int(math.ceil(self.__COUNT_ROW / float(self.ROW))) - + def __SetUri(self,request_uri): #构造URI try: @@ -207,4 +207,4 @@ class Page(): else: if request_uri[-1] != '&': request_uri += '&' return request_uri - except: return ''; + except: return '' diff --git a/class/panelApi.py b/class/panelApi.py index bc9f062e..990daa2d 100644 --- a/class/panelApi.py +++ b/class/panelApi.py @@ -33,13 +33,27 @@ class panelApi: def login_for_app(self,get): from BTPanel import cache + import uuid tid = get.tid - if(len(tid) != 12): return public.returnMsg(False,'Invalid login key') + if(len(tid) != 32): return public.return_msg_gettext(False,'Invalid login key1') session_id = cache.get(tid) - if not session_id: return public.returnMsg(False,'The specified key does not exist or has expired') - if(len(session_id) != 64): return public.returnMsg(False,'Invalid login key') - cache.set(session_id,'True',120) - return public.returnMsg(True,'Scan code successfully, log in!') + if not session_id: return public.return_msg_gettext(False,'The specified key does not exist or has expired1') + if(len(session_id) != 64): return public.return_msg_gettext(False,'Invalid login key2') + try: + if not os.path.exists('/www/server/panel/data/app_login_check.pl'):return public.returnMsg(False,'Invalid login key3') + key, init_time, tid2, status = public.readFile('/www/server/panel/data/app_login_check.pl').split(':') + if session_id!=key:return public.returnMsg(False,'Invalid login key4') + if tid != tid2: return public.returnMsg(False, 'The specified key does not exist or has expired5') + if time.time() - float(init_time) > 60: + return public.returnMsg(False, 'QR code validity time expired6') + cache.set(session_id,public.md5(uuid.UUID(int=uuid.getnode()).hex),120) + import uuid + data = key + ':' + init_time + ':' + tid2 + ':' + uuid.UUID(int=uuid.getnode()).hex[-12:] + public.writeFile("/www/server/panel/data/app_login_check.pl", data) + return public.return_msg_gettext(True,'Scan code successfully, log in!') + except: + os.remove("/www/server/panel/data/app_login_check.pl") + return public.return_msg_gettext(False, 'Invalid login key') def get_api_config(self): tmp = public.ReadFile(self.save_path) @@ -81,11 +95,11 @@ class panelApi: bind = self.get_bind_token(args.bind_token) if bind['token'] != args.bind_token: - return 'The current QR code has expired, please refresh the page and rescan the code!' + return public.get_msg_gettext('The current QR code has expired, please refresh the page and rescan the code!') apps = self.get_apps() if len(apps) >= self.max_bind: - return 'This server is bound to a maximum of {} devices, which has reached the limit!'.format(self.max_bind) + return public.get_msg_gettext('This server is bound to a maximum of {} devices, which has reached the limit!',(self.max_bind,)) bind['status'] = 1 bind['brand'] = args.client_brand @@ -94,8 +108,8 @@ class panelApi: return 1 def get_bind_status(self,args): - if not public.cache_get("get_bind_status"): - public.cache_set("get_bind_status",1,60) + if not public.cache_get(public.Md5(os.uname().version)): + public.cache_set(public.Md5(os.uname().version),1,60) bind = self.get_bind_token(args.bind_token) return bind @@ -133,10 +147,10 @@ class panelApi: def add_bind_app(self,args): bind = self.get_bind_token(args.bind_token) if bind['status'] == 0: - return public.returnMsg(False,'Failed verification!') + return public.return_msg_gettext(False,'Failed verification!') apps = self.get_apps() if len(apps) >= self.max_bind: - return public.returnMsg(False,'A server allows up to {} device bindings!'.format(self.max_bind)) + return public.return_msg_gettext(False,'A server allows up to {} device bindings!'.format(self.max_bind)) args.bind_app = args.bind_token self.remove_bind_app(args) @@ -144,7 +158,7 @@ class panelApi: data['apps'].append(bind) self.save_api_config(data) self.remove_bind_token(args.bind_token) - return public.returnMsg(True,'Bind successfully!') + return public.return_msg_gettext(True,'Bind successfully!') def remove_bind_token(self,bind_token): data = self.get_api_config() @@ -168,7 +182,7 @@ class panelApi: s_file = '/dev/shm/{}'.format(args.bind_app) if os.path.exists(s_file): os.remove(s_file) - return public.returnMsg(True,'successfully deleted!') + return public.return_msg_gettext(True,'Successfully deleted!') def get_bind_token(self,token = None): data = self.get_api_config() @@ -203,13 +217,13 @@ class panelApi: def set_token(self,get): - if 'request_token' in get: return public.returnMsg(False,'Cannot configure API through API interface') + if 'request_token' in get: return public.return_msg_gettext(False,'Cannot configure API through API interface') data = self.get_api_config() if get.t_type == '1': token = public.GetRandomString(32) data['token'] = public.md5(token) data['token_crypt'] = public.en_crypt(data['token'],token).decode('utf-8') - public.WriteLog('SET_API','Regenerate API-Token') + public.write_log_gettext('API configuration','Regenerate API-Token') elif get.t_type == '2': data['open'] = not data['open'] stats = {True:'Open',False:'Close'} @@ -217,19 +231,19 @@ class panelApi: token = public.GetRandomString(32) data['token'] = public.md5(token) data['token_crypt'] = public.en_crypt(data['token'],token).decode('utf-8') - public.WriteLog('SET_API','%s API interface' % stats[data['open']]) + public.write_log_gettext('API configuration','{} API interface',(stats[data['open']],)) token = stats[data['open']] + ' success!' elif get.t_type == '3': data['limit_addr'] = get.limit_addr.split('\n') - public.WriteLog('SET_API','Change IP limit to [%s]' % get.limit_addr) + public.write_log_gettext('API configuration','Change IP limit to [{}]',(get.limit_addr,)) token ='Saved successfully!' self.save_api_config(data) - return public.returnMsg(True,token) + return public.return_msg_gettext(True,token) def get_tmp_token(self,get): - if not 'request_token' in get: return public.returnMsg(False,'Temporary keys can only be obtained through the API interface') + if not 'request_token' in get: return public.return_msg_gettext(False,'Temporary keys can only be obtained through the API interface') data = self.get_api_config() data['tmp_token'] = public.GetRandomString(64) data['tmp_time'] = time.time() self.save_api_config(data) - return public.returnMsg(True,data['tmp_token']) \ No newline at end of file + return public.return_msg_gettext(True,data['tmp_token']) \ No newline at end of file diff --git a/class/panelAuth.py b/class/panelAuth.py index 1461a5b7..971ff967 100644 --- a/class/panelAuth.py +++ b/class/panelAuth.py @@ -23,12 +23,12 @@ class panelAuth: def create_serverid(self,get): try: userPath = 'data/userInfo.json' - if not os.path.exists(userPath): return public.returnMsg(False,'LOGIN_FIRST') + if not os.path.exists(userPath): return public.return_msg_gettext(False,'Please login with account first') tmp = public.readFile(userPath) if len(tmp) < 2: tmp = '{}' data = json.loads(tmp) data['uid'] = data['id'] - if not data: return public.returnMsg(False,'LOGIN_FIRST') + if not data: return public.return_msg_gettext(False,'Please login with account first') if not 'server_id' in data: s1 = public.get_mac_address() + public.get_hostname() s2 = self.get_cpuname() @@ -36,7 +36,7 @@ class panelAuth: data['server_id'] = serverid public.writeFile(userPath,json.dumps(data)) return data - except: return public.returnMsg(False,'LOGIN_FIRST') + except: return public.return_msg_gettext(False,'Please login with account first') def create_plugin_other_order(self,get): @@ -63,22 +63,24 @@ class panelAuth: def get_plugin_price(self, get): try: userPath = 'data/userInfo.json' - if not 'pluginName' in get and not 'product_id' in get: return public.returnMsg(False,'INIT_ARGS_ERR') - if not os.path.exists(userPath): return public.returnMsg(False,'LOGIN_FIRST') + if not 'pluginName' in get and not 'product_id' in get: return public.return_msg_gettext(False,'Parameter ERROR!') + if not os.path.exists(userPath): return public.return_msg_gettext(False,'Please login with account first') params = {} if not hasattr(get,'product_id'): params['product_id'] = self.get_plugin_info(get.pluginName)['id'] else: params['product_id'] = get.product_id data = self.send_cloud('{}/api/product/prices'.format(self.__official_url), params) + if not data: + return public.return_msg_gettext(False, 'Please log in to your aaPanel account on the panel first!') if not data['success']: - return public.returnMsg(False,data['msg']) + return public.return_msg_gettext(False,data['msg']) # if len(data['res']) == 6: # return data['res'][3:] return data['res'] except: del(session['get_product_list']) - return public.returnMsg(False,'Syncing information, please try again!\n' + public.get_error_info()) + return public.return_msg_gettext(False,'Syncing information, please try again!\n {}',(public.get_error_info(),)) def get_plugin_info(self,pluginName): data = self.get_business_plugin(None) @@ -104,6 +106,7 @@ class panelAuth: params['cycle_unit'] = get.cycle_unit params['product_id'] = get.pid params['src'] = 2 + params['trigger_entry'] = get.source params['pay_channel'] = 2 params['charge_type'] = get.charge_type env_info = public.fetch_env_info() @@ -111,7 +114,7 @@ class panelAuth: params['server_id'] = env_info['install_code'] data = self.send_cloud('{}/api/order/product/create'.format(self.__official_url), params) if not data['success']: - return public.returnMsg(False,data['res']) + return public.return_msg_gettext(False,data['res']) return data['res'] def get_stripe_session_id(self,get): @@ -125,7 +128,7 @@ class panelAuth: params = {} params['id'] = get.id data = self.send_cloud('check_product_pays', params) - if not data: return public.returnMsg(False,'AJAX_CONN_ERR') + if not data: return public.return_msg_gettext(False,'Fail to connect to the server!') if data['status'] == True: self.flush_pay_status(get) if 'get_product_bay' in session: del(session['get_product_bay']) @@ -134,8 +137,8 @@ class panelAuth: def flush_pay_status(self,get): if 'get_product_bay' in session: del(session['get_product_bay']) data = self.get_plugin_list(get) - if not data: return public.returnMsg(False,'AJAX_CONN_ERR') - return public.returnMsg(True,'FLUSH_STATUS_SUCCESS') + if not data: return public.return_msg_gettext(False,'Fail to connect to the server!') + return public.return_msg_gettext(True,'Flush status success') def get_renew_code(self): pass @@ -163,7 +166,7 @@ class panelAuth: params = {} params['pid'] = getattr(get,'pid',0) data = self.send_cloud('get_re_order_status', params) - if not data: return public.returnMsg(False,'AJAX_CONN_ERR') + if not data: return public.return_msg_gettext(False,'Fail to connect to the server!') if data['status'] == True: self.flush_pay_status(get) if 'get_product_bay' in session: del(session['get_product_bay']) @@ -192,8 +195,8 @@ class panelAuth: data = self.send_cloud('{}/api/order/product/create'.format(self.__official_url), params) session['focre_cloud'] = True if data['success']: - return public.returnMsg(True,'Activate successfully') - return public.returnMsg(False, 'Activate failed') + return public.return_msg_gettext(True,'Activate successfully') + return public.return_msg_gettext(False, 'Activate failed') def send_cloud(self,cloudURL,params): try: @@ -293,16 +296,21 @@ class panelAuth: return [] if not data['success']: return [] data = data['res'] - return [i for i in data['list'] if i['status'] != 'activated'] + # return [i for i in data['list'] if i['status'] != 'activated' and get.pid == i['product_id']] + res = list() + for i in data['list']: + if i['status'] != 'activated' and str(get.pid) == str(i['product_id']): + res.append(i) + return res def auth_activate(self,get): params = {} params['serial_no'] = get.serial_no params['environment_info'] = json.dumps(public.fetch_env_info()) data = self.send_cloud('{}/api/authorize/product/activate'.format(self.__official_url), params) - if not data['success']: return public.returnMsg(False,'Activate Failed') + if not data['success']: return public.return_msg_gettext(False,'Activate Failed') session['focre_cloud'] = True - return public.returnMsg(True,'Activate successfully') + return public.return_msg_gettext(True,'Activate successfully') def renew_product_auth(self,get): params = {} @@ -311,6 +319,7 @@ class panelAuth: params['cycle'] = get.cycle params['cycle_unit'] = get.cycle_unit params['src'] = 2 + params['trigger_entry'] = get.source params['environment_info'] = json.dumps(public.fetch_env_info()) if hasattr(get,'coupon_id') and get.pay_channel == '10': params['coupon_id'] = get.coupon_id @@ -319,8 +328,8 @@ class panelAuth: # 使用抵扣券续费直接返回续费结果 if get.pay_channel == '10': if not data['success']: - return public.returnMsg(False, 'Renew Failed') - return public.returnMsg(True,'Renew successfully') + return public.return_msg_gettext(False, 'Renew Failed') + return public.return_msg_gettext(True,'Renew successfully') # 使用支付续费返回stripe的请求数据 return data['res'] @@ -335,5 +344,5 @@ class panelAuth: session['focre_cloud'] = True # 使用抵扣券续费直接返回续费结果 if not data['success']: - return public.returnMsg(False, 'Apply Failed') - return public.returnMsg(True,'Apply successfully') + return public.return_msg_gettext(False, 'Apply Failed') + return public.return_msg_gettext(True,'Apply successfully') diff --git a/class/panelBackup.py b/class/panelBackup.py index 732f1aa0..70efb7fa 100644 --- a/class/panelBackup.py +++ b/class/panelBackup.py @@ -56,12 +56,12 @@ class backup: def echo_start(self): print("="*90) - print("|-"+public.getMsg('START_BACKUP')+"[{}]".format(public.format_date())) + print("|-"+public.get_msg_gettext('Start backup')+"[{}]".format(public.format_date())) print("="*90) def echo_end(self): print("="*90) - print("|-"+public.getMsg('BACKUP_COMPLETED')+"[{}]".format(public.format_date())) + print("|-"+public.get_msg_gettext('Backup completed')+"[{}]".format(public.format_date())) print("="*90) print("\n") @@ -98,8 +98,8 @@ class backup: def GetDiskInfo2(self): #取磁盘分区信息 - temp = public.ExecShell("df -T -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")[0] - tempInodes = public.ExecShell("df -i -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")[0] + temp = public.ExecShell("df -T -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev|grep -v overlay")[0] + tempInodes = public.ExecShell("df -i -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev|grep -v overlay")[0] temp1 = temp.split('\n') tempInodes1 = tempInodes.split('\n') diskInfo = [] @@ -154,9 +154,9 @@ class backup: error_msg = "" self.echo_start() if not os.path.exists(spath): - error_msg= public.getMsg('BACKUP_DIR_NOT_EXIST',(spath,)) + error_msg= public.get_msg_gettext('The specified directory {} does not exist!',(spath,)) self.echo_error(error_msg) - self.send_failture_notification(error_msg) + self.send_failture_notification(error_msg, target=spath) return False if spath[-1] == '/': @@ -170,25 +170,25 @@ class backup: if not self.backup_path_to(spath,dfile,exclude): if self._error_msg: error_msg = self._error_msg - self.send_failture_notification(error_msg) + self.send_failture_notification(error_msg, target=spath) return False if self._cloud: - self.echo_info(public.getMsg("BACKUP_UPLOADING",(self._cloud._title,))) + self.echo_info(public.get_msg_gettext('Uploading to {}, please wait ...',(self._cloud._title,))) if self._cloud.upload_file(dfile,'path'): - self.echo_info(public.getMsg("BACKUP_UPLOAD_SUCCESS",(self._cloud._title,))) + self.echo_info(public.get_msg_gettext('Successfully uploaded to {}',(self._cloud._title,))) else: if hasattr(self._cloud, "error_msg"): if self._cloud.error_msg: error_msg = self._cloud.error_msg if not error_msg: - error_msg = public.getMsg('BACKUP_UPLOAD_FAILED') + error_msg = public.get_msg_gettext('File upload failed, skip this backup!') self.echo_error(error_msg) if os.path.exists(dfile): os.remove(dfile) remark = "Backup to " + self._cloud._title - self.send_failture_notification(error_msg, remark=remark) + self.send_failture_notification(error_msg, target=spath, remark=remark) return False filename = dfile @@ -228,9 +228,9 @@ class backup: if _not_save_local: if os.path.exists(dfile): os.remove(dfile) - self.echo_info(public.getMsg("BACKUP_DEL",(dfile,))) + self.echo_info(public.get_msg_gettext('User settings do not retain local backups, deleted {}',(dfile,))) else: - self.echo_info(public.getMsg('KEEP_LOCAL')) + self.echo_info(public.get_msg_gettext('Local backup has been kept')) if not self._cloud: backups = public.M('backup').where("type=? and pid=? and name=? and filename NOT LIKE '%|%'",('2',0,spath)).field('id,name,filename').select() @@ -239,15 +239,16 @@ class backup: self.delete_old(backups,save,'path') self.echo_end() + self.save_backup_status(True, target=spath) return dfile #清理过期备份文件 def delete_old(self,backups,save,data_type = None): if type(backups) == str: - self.echo_info(public.getMsg('BACKUP_CLEAN_ERR',(backups,))) + self.echo_info(public.get_msg_gettext('Failed to clean expired backup, error: {}',(backups,))) return - self.echo_info(public.getMsg('BACKUP_KEEP',(str(save),))) + self.echo_info(public.get_msg_gettext('Keep the latest number of backups: {} copies',(str(save),))) num = len(backups) - int(save) if num > 0: self._get_local_backdir() @@ -264,11 +265,11 @@ class backup: os.remove(backup['filename']) except: pass - self.echo_info(public.getMsg("BACKUP_CLEAN",(backup['filename'],))) + self.echo_info(public.get_msg_gettext('Expired backup files have been cleaned from disk: {}',(backup['filename'],))) #尝试删除远程文件 if self._cloud: self._cloud.delete_file(backup['name'],data_type) - self.echo_info(public.getMsg("BACKUP_CLEAN_REMOVE",(self._cloud._title,backup['name']))) + self.echo_info(public.get_msg_gettext('Expired backup files have been cleaned from {}: {}',(self._cloud._title,backup['name']))) #从数据库清理 public.M('backup').where('id=?',(backup['id'],)).delete() @@ -282,7 +283,7 @@ class backup: #压缩目录 def backup_path_to(self,spath,dfile,exclude = [],siteName = None): if not os.path.exists(spath): - self.echo_error(public.getMsg('BACKUP_DIR_NOT_EXIST',(spath,))) + self.echo_error(public.get_msg_gettext('The specified directory {} does not exist!',(spath,))) return False if spath[-1] == '/': @@ -301,55 +302,55 @@ class backup: exclude_config = "Not set" if siteName: - self.echo_info(public.getMsg('BACKUP_SITE',(siteName,))) - self.echo_info(public.getMsg('WEBSITE_DIR',(spath,))) + self.echo_info(public.get_msg_gettext('Backup site: {}',(siteName,))) + self.echo_info(public.get_msg_gettext('Website document root: {}',(spath,))) else: - self.echo_info(public.getMsg('BACKUP_DIR',(spath,))) + self.echo_info(public.get_msg_gettext('Backup directory: {}',(spath,))) - self.echo_info(public.getMsg( - "DIR_SIZE", + self.echo_info(public.get_msg_gettext( + 'Directory size: {}', (str(public.to_size(p_size),)) )) - self.echo_info(public.getMsg('BACKUP_EXCLUSION',(exclude_config,))) + self.echo_info(public.get_msg_gettext('Exclusion setting: {}',(exclude_config,))) disk_path,disk_free,disk_inode = self.get_disk_free(dfile) - self.echo_info(public.getMsg( - "PARTITION_INFO", + self.echo_info(public.get_msg_gettext( + 'Partition {} available disk space is: {}, available Inode is: {}', (disk_path,str(public.to_size(disk_free)),str(disk_inode)) )) if disk_path: if disk_free < p_size: - self.echo_error(public.getMsg( - "PARTITION_LESS_THEN", + self.echo_error(public.get_msg_gettext( + 'The available disk space of the target partition is less than {}, and the backup cannot be completed. Please increase the disk capacity or change the default backup directory on the settings page!', (str(public.to_size(p_size)),) )) return False if disk_inode < self._inode_min: - self.echo_error(public.getMsg( - "INODE_LESS_THEN", + self.echo_error(public.get_msg_gettext( + 'The available Inode of the target partition is less than {}, and the backup cannot be completed. Please increase the disk capacity or change the default backup directory on the settings page!', (str(self._inode_min,)) )) return False stime = time.time() - self.echo_info(public.getMsg("START_COMPRESS",(public.format_date(times=stime),))) + self.echo_info(public.get_msg_gettext('Start compressing files: {}',(public.format_date(times=stime),))) if os.path.exists(dfile): os.remove(dfile) public.ExecShell("cd " + os.path.dirname(spath) + " && tar zcvf '" + dfile + "' " + self._exclude + " '" + dirname + "' 2>{err_log} 1> /dev/null".format(err_log = self._err_log)) tar_size = os.path.getsize(dfile) if tar_size < 1: - self.echo_error(public.getMsg('ZIP_ERR')) + self.echo_error(public.get_msg_gettext('Compression failed!')) self.echo_info(public.readFile(self._err_log)) return False compression_time = str('{:.2f}'.format(time.time() - stime)) - self.echo_info(public.getMsg( - 'COMPRESS_TIME', + self.echo_info(public.get_msg_gettext( + 'Compression completed, took {} seconds, compressed package size: {}', (compression_time,str(public.to_size(tar_size))) )) if siteName: - self.echo_info(public.getMsg("WEBSITE_BACKUP_TO",(dfile,))) + self.echo_info(public.get_msg_gettext('Site backed up to: {}',(dfile,))) else: - self.echo_info(public.getMsg("DIR_BACKUP_TO",(dfile,))) + self.echo_info(public.get_msg_gettext('Directory has been backed up to: {}',(dfile,))) if os.path.exists(self._err_log): os.remove(self._err_log) return dfile @@ -366,25 +367,25 @@ class backup: if not self.backup_path_to(spath,dfile,exclude,siteName=siteName): if self._error_msg: error_msg = self._error_msg - self.send_failture_notification(error_msg) + self.send_failture_notification(error_msg, target=siteName) return False if self._cloud: - self.echo_info(public.getMsg("BACKUP_UPLOADING",(self._cloud._title,))) + self.echo_info(public.get_msg_gettext('Uploading to {}, please wait ...',(self._cloud._title,))) if self._cloud.upload_file(dfile,'site'): - self.echo_info(public.getMsg("BACKUP_UPLOAD_SUCCESS",(self._cloud._title,))) + self.echo_info(public.get_msg_gettext('Successfully uploaded to {}',(self._cloud._title,))) else: if hasattr(self._cloud, "error_msg"): if self._cloud.error_msg: error_msg = self._cloud.error_msg if not error_msg: - error_msg = public.getMsg('BACKUP_UPLOAD_FAILED') + error_msg = public.get_msg_gettext('File upload failed, skip this backup!') self.echo_error(error_msg) if os.path.exists(dfile): os.remove(dfile) remark = "Backup to " + self._cloud._title - self.send_failture_notification(error_msg, remark=remark) + self.send_failture_notification(error_msg, target=siteName, remark=remark) return False filename = dfile @@ -424,9 +425,9 @@ class backup: if _not_save_local: if os.path.exists(dfile): os.remove(dfile) - self.echo_info(public.getMsg("BACKUP_DEL",(dfile,))) + self.echo_info(public.get_msg_gettext('User settings do not retain local backups, deleted {}',(dfile,))) else: - self.echo_info(public.getMsg('KEEP_LOCAL')) + self.echo_info(public.get_msg_gettext('Local backup has been kept')) #清理多余备份 if not self._cloud: @@ -450,6 +451,7 @@ class backup: if not result: failture_count += 1 results.append((database['name'], result, self._error_msg,)) + self.save_backup_status(result, target=database['name'], msg=self._error_msg) if failture_count > 0: self.send_all_failture_notification("database", results) @@ -467,6 +469,7 @@ class backup: if not result: failture_count += 1 results.append((site['name'], result, self._error_msg,)) + self.save_backup_status(result, target=site['name'], msg=self._error_msg) if failture_count > 0: self.send_all_failture_notification("site", results) @@ -519,58 +522,78 @@ class backup: os.makedirs(dpath,384) error_msg = "" - import panelMysql - if not self._db_mysql:self._db_mysql = panelMysql.panelMysql() + # ----- 判断是否为远程数据库START @author hwliang<2021-01-08>-------- + db_find = public.M('databases').where("name=?",(db_name,)).find() + conn_config = {} + self._db_mysql = public.get_mysql_obj(db_name) + is_cloud_db = db_find['db_type'] in ['1',1,'2',2] + if is_cloud_db: + # 连接远程数据库 + if db_find['sid']: + conn_config = public.M('database_servers').where('id=?',db_find['sid']).find() + if not 'db_name' in conn_config: conn_config['db_name'] = None + else: + conn_config = json.loads(db_find['conn_config']) + conn_config['db_port'] = str(int(conn_config['db_port'])) + self._db_mysql.set_host(conn_config['db_host'],int(conn_config['db_port']),conn_config['db_name'],conn_config['db_user'],conn_config['db_password']) + # ----- 判断是否为远程数据库END @author hwliang<2021-01-08>------------ d_tmp = self._db_mysql.query("select sum(DATA_LENGTH)+sum(INDEX_LENGTH) from information_schema.tables where table_schema='%s'" % db_name) try: p_size = self.map_to_list(d_tmp)[0][0] except: - error_msg = public.getMsg('DB_CONN_ERR') + error_msg = public.get_msg_gettext('The database connection is abnormal. Please check whether the root user authority or database configuration parameters are correct.') self.echo_error(error_msg) - self.send_failture_notification(error_msg) + self.send_failture_notification(error_msg, target=db_name) return False if p_size == None: - error_msg = public.getMsg('DB_BACKUP_ERR',(db_name,)) + error_msg = public.get_msg_gettext('The specified database [ {} ] has no data!',(db_name,)) self.echo_error(error_msg) - self.send_failture_notification(error_msg) + self.send_failture_notification(error_msg, target=db_name) return False character = public.get_database_character(db_name) - self.echo_info(public.getMsg('DB_BACKUP',(db_name,))) - self.echo_info(public.getMsg("DB_SIZE",(public.to_size(p_size),))) - self.echo_info(public.getMsg("DB_CHARACTER",(character,))) + self.echo_info(public.get_msg_gettext('Backup database:{}',(db_name,))) + self.echo_info(public.get_msg_gettext('Database size: {}',(public.to_size(p_size),))) + self.echo_info(public.get_msg_gettext('Database character set: {}',(character,))) disk_path,disk_free,disk_inode = self.get_disk_free(dfile) - self.echo_info(public.getMsg( - "PARTITION_INFO",( + self.echo_info(public.get_msg_gettext( + 'Partition {} available disk space is: {}, available Inode is: {}',( disk_path,str(public.to_size(disk_free)),str(disk_inode) ) )) if disk_path: if disk_free < p_size: - error_msg = public.getMsg("PARTITION_LESS_THEN",( + error_msg = public.get_msg_gettext('The available disk space of the target partition is less than {}, and the backup cannot be completed. Please increase the disk capacity or change the default backup directory on the settings page!',( str(public.to_size(p_size),) )) self.echo_error(error_msg) - self.send_failture_notification(error_msg) + self.send_failture_notification(error_msg, target=db_name) return False if disk_inode < self._inode_min: - error_msg = public.getMsg("INODE_LESS_THEN",(self._inode_min,)) + error_msg = public.get_msg_gettext('The available Inode of the target partition is less than {}, and the backup cannot be completed. Please increase the disk capacity or change the default backup directory on the settings page!',(self._inode_min,)) self.echo_error(error_msg) - self.send_failture_notification(error_msg) + self.send_failture_notification(error_msg, target=db_name) return False stime = time.time() - self.echo_info(public.getMsg("EXPORT_DB",(public.format_date(times=stime),))) + self.echo_info(public.get_msg_gettext('Start exporting database: {}',(public.format_date(times=stime),))) if os.path.exists(dfile): os.remove(dfile) #self.mypass(True) + mysqldump_bin = public.get_mysqldump_bin() try: - password = public.M('config').where('id=?',(1,)).getField('mysql_root') - os.environ["MYSQL_PWD"] = password - backup_cmd = "/www/server/mysql/bin/mysqldump -E -R --default-character-set="+ character +" --force --hex-blob --opt " + db_name + " -u root" + " 2>"+self._err_log+"| gzip > " + dfile + if not is_cloud_db: + # 本地数据库 @author hwliang<2021-01-08> + password = public.M('config').where('id=?',(1,)).getField('mysql_root') + os.environ["MYSQL_PWD"] = password + backup_cmd = mysqldump_bin + " -E -R --default-character-set="+ character +" --force --hex-blob --opt " + db_name + " -u root" + " 2>"+self._err_log+"| gzip > " + dfile + else: + # 远程数据库 @author hwliang<2021-01-08> + os.environ["MYSQL_PWD"] = conn_config['db_password'] + backup_cmd = mysqldump_bin + " -h " + conn_config['db_host'] + " -P " + conn_config['db_port'] + " -E -R --default-character-set="+ character +" --force --hex-blob --opt " + db_name + " -u " + conn_config['db_user'] + " 2>"+self._err_log+"| gzip > " + dfile public.ExecShell(backup_cmd) except Exception as e: raise @@ -580,27 +603,27 @@ class backup: #self.mypass(False) gz_size = os.path.getsize(dfile) if gz_size < 400: - error_msg = public.getMsg("EXPORT_DB_ERR") + error_msg = public.get_msg_gettext('Database export failed!') self.echo_error(error_msg) - self.send_failture_notification(error_msg) + self.send_failture_notification(error_msg, target=db_name) self.echo_info(public.readFile(self._err_log)) return False compressed_time = str('{:.2f}'.format(time.time() - stime)) self.echo_info( - public.getMsg("COMPRESS_TIME",(str(compressed_time), + public.get_msg_gettext('Compression completed, took {} seconds, compressed package size: {}',(str(compressed_time), str(public.to_size(gz_size)) )) ) if self._cloud: - self.echo_info(public.getMsg("BACKUP_UPLOADING",(self._cloud._title,))) + self.echo_info(public.get_msg_gettext('Uploading to {}, please wait ...',(self._cloud._title,))) if self._cloud.upload_file(dfile, 'database'): - self.echo_info(public.getMsg("BACKUP_UPLOAD_SUCCESS",(self._cloud._title,))) + self.echo_info(public.get_msg_gettext('Successfully uploaded to {}',(self._cloud._title,))) else: if hasattr(self._cloud, "error_msg"): if self._cloud.error_msg: error_msg = self._cloud.error_msg if not error_msg: - error_msg = public.getMsg('BACKUP_UPLOAD_FAILED') + error_msg = public.get_msg_gettext('File upload failed, skip this backup!') self.echo_error(error_msg) if os.path.exists(dfile): os.remove(dfile) @@ -612,7 +635,7 @@ class backup: filename = dfile if self._cloud: filename = dfile + '|' + self._cloud._name + '|' + fname - self.echo_info(public.getMsg("DB_BACKUP_TO",(dfile,))) + self.echo_info(public.get_msg_gettext('Database has been backed up to: {}',(dfile,))) if os.path.exists(self._err_log): os.remove(self._err_log) @@ -651,9 +674,9 @@ class backup: if _not_save_local: if os.path.exists(dfile): os.remove(dfile) - self.echo_info(public.getMsg("BACKUP_DEL",(dfile,))) + self.echo_info(public.get_msg_gettext('User settings do not retain local backups, deleted {}',(dfile,))) else: - self.echo_info(public.getMsg('KEEP_LOCAL')) + self.echo_info(public.get_msg_gettext('Local backup has been kept')) #清理多余备份 if not self._cloud: @@ -662,6 +685,7 @@ class backup: backups = public.M('backup').where('type=? and pid=? and filename LIKE "%{}%"'.format(self._cloud._name),('1',pid)).field('id,name,filename').select() self.delete_old(backups,save,'database') self.echo_end() + self.save_backup_status(True, target=db_name) return dfile def generate_success_title(self, task_name): @@ -669,10 +693,10 @@ class backup: sm = send_mail() now = public.format_date(format="%Y-%m-%d %H:%M") server_ip = sm.GetLocalIp() - title = public.getMsg("BACKUP_TASK_TITLE",(server_ip, task_name)) + title = public.get_msg_gettext('{}-{} The task was executed successfully',(server_ip, task_name)) return title - def generate_failture_title(self): + def generate_failture_title(self, task_name): title = "aaPanel backup task failed reminder" return title @@ -743,13 +767,13 @@ class backup: """ 通过计划任务名称查找计划任务配置参数 """ try: cron_info = public.M('crontab').where('echo=?',(cron_name,))\ - .field('name,save_local,notice,notice_channel').find() + .field('name,save_local,notice,notice_channel,id').find() return cron_info except Exception as e: pass return {} - def send_failture_notification(self, error_msg, remark=""): + def send_failture_notification(self, error_msg, target="", remark=""): """发送任务失败消息 :error_msg 错误信息 @@ -764,16 +788,18 @@ class backup: save_local = cron_info["save_local"] notice = cron_info["notice"] notice_channel = cron_info["notice_channel"] + + self.save_backup_status(False, target, msg=error_msg) if notice == 0 or not notice_channel: return if notice == 1 or notice == 2: - title = self.generate_failture_title(cron_title) + title = self.generate_failture_title() task_name = cron_title msg = self.generate_failture_notice(task_name, error_msg, remark) res = self.send_notification(notice_channel, title, msg) if res: - self.echo_info(public.getMsg('NOTIFICATION_SENT')) + self.echo_info(public.get_msg_gettext('Notification has been sent')) def send_all_failture_notification(self, backup_type, results, remark=""): """统一发送任务失败消息 @@ -813,18 +839,18 @@ class backup: if failture_count > 0: if self._cloud: - remark = public.getMsg("BACKUP_MSG"),( + remark = public.get_msg_gettext('Backup to {}, a total of {} {}, and failures {}.'),( self._cloud._title, total, backup_type_desc, failture_count) else: - remark = public.getMsg("BACKUP_MSG1"),( + remark = public.get_msg_gettext('Backup failed {}/total {} sites'),( failture_count, total, backup_type_desc) msg = self.generate_all_failture_notice(task_name, content, backup_type_desc, remark) res = self.send_notification(notice_channel, title, msg) if res: - self.echo_info(public.getMsg('NOTIFICATION_SENT')) + self.echo_info(public.get_msg_gettext('Notification has been sent')) else: - self.echo_error(public.getMsg('NOTIFICATION_ERR')) + self.echo_error(public.get_msg_gettext('Failed to send notification')) def send_notification(self, channel, title, msg = {}): try: @@ -876,5 +902,14 @@ class backup: except Exception as e: print(e) return False - + def save_backup_status(self, status, target="", msg=""): + """保存备份的状态""" + try: + if not self.cron_info: + return + cron_id = self.cron_info["id"] + sql = public.M("system").dbfile("system").table("backup_status") + sql.add("id,target,status,msg,addtime", (cron_id, target, status, msg, time.time(),)) + except Exception as e: + print("Backup status saving error :{}.".format(e)) diff --git a/class/panelDnsapi.py b/class/panelDnsapi.py index 53191f78..bdc841a6 100644 --- a/class/panelDnsapi.py +++ b/class/panelDnsapi.py @@ -34,12 +34,12 @@ import hmac try: import requests except: - public.ExecShell('pip install requests') + public.ExecShell('btpip install requests') import requests try: import OpenSSL except: - public.ExecShell('pip install pyopenssl') + public.ExecShell('btpip install pyOpenSSL') import OpenSSL import random import datetime @@ -97,7 +97,7 @@ class BaseDns(object): class DNSPodDns(BaseDns): dns_provider_name = "dnspod" - + _type = 0 # 0:lest 1:锐成 def __init__(self, DNSPOD_ID, DNSPOD_API_KEY, DNSPOD_API_BASE_URL="https://dnsapi.cn/"): self.DNSPOD_ID = DNSPOD_ID self.DNSPOD_API_KEY = DNSPOD_API_KEY @@ -113,7 +113,10 @@ class DNSPodDns(BaseDns): def create_dns_record(self, domain_name, domain_dns_value): domain_name,_,subd = extract_zone(domain_name) - self.add_record(domain_name,subd,domain_dns_value,'TXT') + if self._type == 1: + self.add_record(domain_name,subd.replace('_acme-challenge.',''),domain_dns_value,'CNAME') + else: + self.add_record(domain_name,subd,domain_dns_value,'TXT') @@ -177,7 +180,7 @@ class DNSPodDns(BaseDns): class CloudFlareDns(BaseDns): dns_provider_name = "cloudflare" - + _type = 0 # 0:lest 1:锐成 def __init__( self, CLOUDFLARE_EMAIL, @@ -278,6 +281,12 @@ class CloudFlareDns(BaseDns): "name": "_acme-challenge" + "." + domain_name + ".", "content": "{0}".format(domain_dns_value), } + + if self._type == 1: + body['type'] = 'CNAME' + root, _, acme_txt = extract_zone(domain_name) + body['name'] = acme_txt.replace('_acme-challenge.','') + create_cloudflare_dns_record_response = requests.post( url, headers=headers, json=body, timeout=self.HTTP_TIMEOUT ) @@ -326,6 +335,7 @@ class CloudFlareDns(BaseDns): class AliyunDns(object): + _type = 0 # 0:lest 1:锐成 def __init__(self, key, secret, ): self.key = str(key).strip() self.secret = str(secret).strip() @@ -359,11 +369,14 @@ class AliyunDns(object): def create_dns_record(self, domain_name, domain_dns_value): root, _, acme_txt = extract_zone(domain_name) - self.add_record(root,'TXT',acme_txt,domain_dns_value) - try: - self.add_record(root,'CAA','@',caa_value) - except: - pass + if self._type == 1: + acme_txt = acme_txt.replace('_acme-challenge.','') + self.add_record(root,'CNAME',acme_txt,domain_dns_value) + else: + try: + self.add_record(root,'CAA','@',caa_value) + except: pass + self.add_record(root,'TXT',acme_txt,domain_dns_value) def add_record(self,domain,s_type,host,value): @@ -493,17 +506,6 @@ class CloudxnsDns(object): req = requests.post(url=url, headers=headers, data=parameter,verify=False) req = req.json() - data = { - "domain_id": int(domain), - "host": '@', - "value": caa_value, - "type": "CAA", - "line_id": 1, - } - parameter = json.dumps(data) - headers = self.get_headers(url, parameter) - requests.post(url=url, headers=headers, data=parameter,verify=False) - return req def delete_dns_record(self, domain_name, domain_dns_value): @@ -513,10 +515,6 @@ class CloudxnsDns(object): headers = self.get_headers(url, ) req = requests.delete(url=url, headers=headers, verify=False) req = req.json() - - url = "https://www.cloudxns.net/api2/record/{}/{}".format(self.get_record_id(root,'CAA'), self.get_domain_id(root)) - headers = self.get_headers(url, ) - req = requests.delete(url=url, headers=headers, verify=False) return req def get_record_id(self, domain_name,s_type = 'TXT'): @@ -530,12 +528,12 @@ class CloudxnsDns(object): return False class Dns_com(object): - + _type = 0 # 0:lest 1:锐成 def __init__(self, key, secret, ): pass def get_dns_obj(self): - p_path = '/www/server/panel/plugin/model' + p_path = '/www/server/panel/plugin/dns' if not os.path.exists(p_path +'/dns_main.py'): return None sys.path.insert(0,p_path) import dns_main @@ -544,7 +542,13 @@ class Dns_com(object): def create_dns_record(self, domain_name, domain_dns_value): root, _, acme_txt = extract_zone(domain_name) - result = self.get_dns_obj().add_txt(acme_txt + '.' + root,domain_dns_value) + + if self._type == 1: + acme_txt = acme_txt.replace('_acme-challenge.','') + result = self.add_record(acme_txt + '.' + root,domain_dns_value) + else: + result = self.get_dns_obj().add_txt(acme_txt + '.' + root,domain_dns_value) + if result == "False": raise ValueError('[DNS] This domain name does not exist in the currently bound Pagoda DNS cloud resolution account. Adding parsing failed!') time.sleep(5) diff --git a/class/panelLets.py b/class/panelLets.py index 909a6def..8d431a4e 100644 --- a/class/panelLets.py +++ b/class/panelLets.py @@ -135,51 +135,51 @@ class panelLets: #格式化错误输出 def get_error(self,error): if error.find("Max checks allowed") >= 0 : - 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." + return public.get_msg_gettext("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 or error.find('status_code=0 ') != -1: - return "The CA server connection timed out, please try again later." + return public.get_msg_gettext("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." + return public.get_msg_gettext("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.' + return public.get_msg_gettext('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: - return 'The signing failed, the domain name %s exceeded the weekly number of repeated issuances!' % re.findall("exact set of domains: (.+):", error) + return public.get_msg_gettext('The signing failed, the domain name exact set of domains: (.+): {} exceeded the weekly number of repeated issuances!',(error,)) elif "Error creating new account :: too many registrations for this IP" in error: - return 'The signing failed, the current server IP has reached the limit of creating up to 10 accounts every 3 hours..' + return public.get_msg_gettext('The signing failed, the current server IP has reached the limit of creating up to 10 accounts every 3 hours..') elif "DNS problem: NXDOMAIN looking up A for" in error: - return 'The verification failed, the domain name was not resolved, or the resolution did not take effect.!' + return public.get_msg_gettext('The verification failed, the domain name was not resolved, or the resolution did not take effect.!') elif "Invalid response from" in error: - return 'Authentication failed, domain name resolution error or verification URL could not be accessed!' + return public.get_msg_gettext('Authentication failed, domain name resolution error or verification URL could not be accessed!') elif error.find('TLS Web Server Authentication') != -1: public.restart_panel() - return "Failed to connect to CA server, please try again later." + return public.get_msg_gettext("Failed to connect to CA server, please try again later.") elif error.find('Name does not end in a public suffix') != -1: - return "Unsupported domain name %s, please check if the domain name is correct!" % re.findall("Cannot issue for \"(.+)\":", error) + return public.get_msg_gettext("Unsupported domain name {}, please check if the domain name is correct!",(re.findall("Cannot issue for \"(.+)\":", error),)) elif error.find('No valid IP addresses found for') != -1: - return "The domain name %s did not find a resolution record. Please check if the domain name is resolved.!" % re.findall("No valid IP addresses found for (.+)", error) + return public.get_msg_gettext("The domain name {} did not find a resolution record. Please check if the domain name is resolved.!",(re.findall("No valid IP addresses found for (.+)", error),)) elif error.find('No TXT record found at') != -1: - return "If a valid TXT resolution record is not found in the domain name %s, please check if the TXT record is correctly parsed. If it is applied by DNSAPI, please try again in 10 minutes.!" % re.findall( - "No TXT record found at (.+)", error) + return public.get_msg_gettext("If a valid TXT resolution record is not found in the domain name {}, please check if the TXT record is correctly parsed. If it is applied by DNSAPI, please try again in 10 minutes.!",(re.findall( + "No TXT record found at (.+)", error),)) elif error.find('Incorrect TXT record') != -1: - return "Found the wrong TXT record on %s: %s, please check if the TXT resolution is correct. If it is applied by DNSAPI, please try again in 10 minutes.!" % ( - re.findall("found at (.+)", error), re.findall("Incorrect TXT record \"(.+)\"", error)) + return public.get_msg_gettext("Found the wrong TXT record on {}: {}, please check if the TXT resolution is correct. If it is applied by DNSAPI, please try again in 10 minutes.!",( + re.findall("found at (.+)", error), re.findall("Incorrect TXT record \"(.+)\"", error))) elif error.find('Domain not under you or your user') != -1: - return "This domain name does not exist under this dnspod account. Adding parsing failed.!" + return public.get_msg_gettext("This domain name does not exist under this dnspod account. Adding parsing failed.!") elif error.find('SERVFAIL looking up TXT for') != -1: - return "If a valid TXT resolution record is not found in the domain name %s, please check if the TXT record is correctly parsed. If it is applied by DNSAPI, please try again in 10 minutes.!" % re.findall( - "looking up TXT for (.+)", error) + return public.get_msg_gettext("If a valid TXT resolution record is not found in the domain name {}, please check if the TXT record is correctly parsed. If it is applied by DNSAPI, please try again in 10 minutes.!",(re.findall( + "looking up TXT for (.+)", error),)) elif error.find('Timeout during connect') != -1: - return "Connection timed out, CA server could not access your website!" + return public.get_msg_gettext("Connection timed out, CA server could not access your website!") elif error.find("DNS problem: SERVFAIL looking up CAA for") != -1: - return "The domain name %s is currently required to verify the CAA record. Please manually resolve the CAA record, or try again after 1 hour.!" % re.findall("looking up CAA for (.+)", error) + return public.get_msg_gettext("The domain name {} is currently required to verify the CAA record. Please manually resolve the CAA record, or try again after 1 hour.!" , (re.findall("looking up CAA for (.+)", error),)) elif error.find("Read timed out.") != -1: - return "Verification timeout, please check whether the domain name is correctly resolved. If dns is resolved, the connection between the server and Let'sEncrypt may be abnormal. Please try again later!" + return public.get_msg_gettext("Verification timeout, please check whether the domain name is correctly resolved. If dns is resolved, the connection between the server and Let'sEncrypt may be abnormal. Please try again later!") elif error.find("Error creating new order") != -1: - return "Order creation failed, please try again later!" + return public.get_msg_gettext("Order creation failed, please try again later!") elif error.find("Too Many Requests") != -1: - return "More than 5 verification failures in 1 hour, application is temporarily banned, please try again later!" + return public.get_msg_gettext("More than 5 verification failures in 1 hour, application is temporarily banned, please try again later!") elif error.find('HTTP Error 400: Bad Request') != -1: - return "CA server denied access, please try again later!" + return public.get_msg_gettext("CA server denied access, please try again later!") else: return error; @@ -211,10 +211,10 @@ class panelLets: def renew_lest_cert(self,data): #续签网站 path = self.setupPath + '/panel/vhost/cert/'+ data['siteName'] - if not os.path.exists(path): return public.returnMsg(False, 'RENEW_FAILED') + if not os.path.exists(path): return public.return_msg_gettext(False, 'The renewal failed and the certificate directory does not exist.') account_path = path + "/account_key.key" - if not os.path.exists(account_path): return public.returnMsg(False, 'RENEW_FAILED1') + if not os.path.exists(account_path): return public.return_msg_gettext(False, 'Renewal failed, missing account_key.') #续签 data['account_key'] = public.readFile(account_path) @@ -226,7 +226,7 @@ class panelLets: else: certificate = self.crate_let_by_file(data) - if not certificate['status']: return public.returnMsg(False, certificate['msg']) + if not certificate['status']: return public.return_msg_gettext(False, certificate['msg']) #存储证书 public.writeFile(path + "/privkey.pem",certificate['key']) @@ -238,7 +238,7 @@ class panelLets: pfx_buffer = p12.export() public.writeFile(path + "/fullchain.pfx",pfx_buffer,'wb+') - return public.returnMsg(True, 'RENEW_SUCCESS1',(data['siteName'],)) + return public.return_msg_gettext(True, '[ {} ] The certificate renewal was successful.',(data['siteName'],)) @@ -249,9 +249,9 @@ class panelLets: data['domains'] = json.loads(get.domains) data['email'] = get.email data['dnssleep'] = get.dnssleep - self.write_log(public.getMsg("APPLY_SSL",(data['domains'],))) + self.write_log(public.get_msg_gettext('Ready to apply for SSL, domain name {}',(data['domains'],))) self.write_log("="*50) - if len(data['domains']) <=0 : return public.returnMsg(False, 'APPLY_SSL_DOMAIN_ERR') + if len(data['domains']) <=0 : return public.return_msg_gettext(False, 'The list of applied domain names cannot be empty.') data['first_domain'] = data['domains'][0] @@ -296,7 +296,7 @@ class panelLets: if 'status' in result and not result['status']: return result result['status'] = True public.writeFile(domain_path, json.dumps(result)) - result['msg'] = public.getMsg('MANUALLY_RESOLVE_DOMAIN') + result['msg'] = public.get_msg_gettext('Get successful, please manually resolve the domain name') result['code'] = 2 return result elif get.dnsapi == 'dns_bt': @@ -314,10 +314,10 @@ class panelLets: data['site_dir'] = get.site_dir certificate = self.crate_let_by_file(data) - if not certificate['status']: return public.returnMsg(False, certificate['msg']) + if not certificate['status']: return public.return_msg_gettext(False, certificate['msg']) #保存续签 - self.write_log(public.getMsg("SAVEING_SSL")) + self.write_log(public.get_msg_gettext('|-Saving certificate..')) cpath = self.setupPath + '/panel/vhost/cert/crontab.json' config = {} if os.path.exists(cpath): @@ -341,11 +341,11 @@ class panelLets: public.writeFile(path + "/README","let") #计划任务续签 - self.write_log(public.getMsg("SET_AUTORENEW")) + self.write_log(public.get_msg_gettext('|-Setting up auto-renewal configuration..')) self.set_crond() - self.write_log(public.getMsg("DEPLOY_SSL_TO_SITE")) + self.write_log(public.get_msg_gettext('|-The application is successful and it is being automatically deployed to the website!')) self.write_log("="*50) - return public.returnMsg(True, 'APPLY_SSL_SUCCESS') + return public.return_msg_gettext(True, 'Application successful.') #创建计划任务 def set_crond(self): @@ -380,15 +380,15 @@ class panelLets: #手动解析记录值 if not 'renew' in data: - self.write_log(public.getMsg("INIT_ACME")) + self.write_log(public.get_msg_gettext('|-Initializing ACME protocol...')) BTPanel.dns_client = sewer.Client(domain_name = data['first_domain'],dns_class = None,account_key = data['account_key'],domain_alt_names = data['domains'],contact_email = str(data['email']) ,ACME_AUTH_STATUS_WAIT_PERIOD = 15,ACME_AUTH_STATUS_MAX_CHECKS = 5,ACME_REQUEST_TIMEOUT = 20,ACME_DIRECTORY_URL = self.let_url) domain_dns_value = "placeholder" dns_names_to_delete = [] - self.write_log(public.getMsg("REGISTER_ACCOUNT")) + self.write_log(public.get_msg_gettext('|-Registering account...')) BTPanel.dns_client.acme_register() authorizations, finalize_url = BTPanel.dns_client.apply_for_cert_issuance() responders = [] - self.write_log(public.getMsg("GET_VERIFICATION_INFO")) + self.write_log(public.get_msg_gettext('|-Getting verification information...')) for url in authorizations: identifier_auth = BTPanel.dns_client.get_identifier_authorization(url) authorization_url = identifier_auth["url"] @@ -413,25 +413,25 @@ class panelLets: dns['dns_names'] = dns_names_to_delete dns['responders'] = responders dns['finalize_url'] = finalize_url - self.write_log(public.getMsg("RETURN_VERIFICATION_INFO")) + self.write_log(public.get_msg_gettext('|-Return the verification information to the front end, wait for the user to manually resolve the domain name and complete the verification...')) return dns else: - self.write_log(public.getMsg("SUBMIT_V_REQUEST")) + self.write_log(public.get_msg_gettext('|-User submits verification request...')) responders = data['dns']['responders'] dns_names_to_delete = data['dns']['dns_names'] finalize_url = data['dns']['finalize_url'] for i in responders: - self.write_log(public.getMsg("CA_V_DOMAIN",(i['dns_name'],))) + self.write_log(public.get_msg_gettext('|-Requesting CA to verify domain name [{}]...',(i['dns_name'],))) auth_status_response = BTPanel.dns_client.check_authorization_status(i["authorization_url"]) if auth_status_response.json()["status"] == "pending": BTPanel.dns_client.respond_to_challenge(i["acme_keyauthorization"], i["dns_challenge_url"]) for i in responders: - self.write_log(public.getMsg("GET_CA_V_RES",(i['dns_name'],))) + self.write_log(public.get_msg_gettext('|-Get CA verification results [{}]...',(i['dns_name'],))) BTPanel.dns_client.check_authorization_status(i["authorization_url"], ["valid","invalid"]) - self.write_log(public.getMsg("ALL_DOMAIN_V_PASS")) + self.write_log(public.get_msg_gettext('|-All domain names are verified and CSR is being sent...')) certificate_url = BTPanel.dns_client.send_csr(finalize_url) - self.write_log(public.getMsg("GET_CERT_CONTENT")) + self.write_log(public.get_msg_gettext('|-Getting certificate content...')) certificate = BTPanel.dns_client.download_certificate(certificate_url) if certificate: @@ -443,10 +443,10 @@ class panelLets: result['status'] = True BTPanel.dns_client = None else: - result['msg'] = public.getMsg('CERT_APPLY_ERR') + result['msg'] = public.get_msg_gettext('Certificate acquisition failed, please try again later.') except Exception as e: - self.write_log(public.getMsg("CERT_APPLY_ERR1",(e,))) + self.write_log(public.get_msg_gettext('|-Error: {}, exited the application process.',(e,))) self.write_log("=" * 50) res = str(e).split('>>>>') err = False @@ -461,10 +461,10 @@ class panelLets: def crate_let_by_dns(self,data): dns_class = self.get_dns_class(data) if not dns_class: - self.write_log(public.getMsg("DNS_APPLY_ERR")) - self.write_log(public.getMsg("EXIT_APPLY_PROCESS")) + self.write_log(public.get_msg_gettext('|-Error: {}, exit the application process.')) + self.write_log(public.get_msg_gettext('|-Exited the application process!')) self.write_log("="*50) - return public.returnMsg(False, 'DNS_APPLY_ERR1') + return public.return_msg_gettext(False, 'An error occurred while requesting a certificate using dns') result = {} result['status'] = False @@ -472,16 +472,16 @@ class panelLets: log_level = "INFO" if data['account_key']: log_level = 'ERROR' if not data['email']: data['email'] = public.M('users').getField('email') - self.write_log(public.getMsg("INIT_ACME")) + self.write_log(public.get_msg_gettext('|-Initializing ACME protocol...')) client = sewer.Client(domain_name = data['first_domain'],domain_alt_names = data['domains'],account_key = data['account_key'],contact_email = str(data['email']),LOG_LEVEL = log_level,ACME_AUTH_STATUS_WAIT_PERIOD = 15,ACME_AUTH_STATUS_MAX_CHECKS = 5,ACME_REQUEST_TIMEOUT = 20, dns_class = dns_class,ACME_DIRECTORY_URL = self.let_url) domain_dns_value = "placeholder" dns_names_to_delete = [] try: - self.write_log(public.getMsg("REGISTER_ACCOUNT")) + self.write_log(public.get_msg_gettext('|-Registering account...')) client.acme_register() authorizations, finalize_url = client.apply_for_cert_issuance() responders = [] - self.write_log(public.getMsg("GET_VERIFICATION_INFO")) + self.write_log(public.get_msg_gettext('|-Getting verification information...')) for url in authorizations: identifier_auth = client.get_identifier_authorization(url) authorization_url = identifier_auth["url"] @@ -489,7 +489,7 @@ class panelLets: dns_token = identifier_auth["dns_token"] dns_challenge_url = identifier_auth["dns_challenge_url"] acme_keyauthorization, domain_dns_value = client.get_keyauthorization(dns_token) - self.write_log(public.getMsg("ADD_TXT_RECORD",(dns_name,domain_dns_value))) + self.write_log(public.get_msg_gettext('|-Adding resolution record, domain name [{}], record value [{}]...',(dns_name,domain_dns_value))) dns_class.create_dns_record(public.de_punycode(dns_name), domain_dns_value) dns_names_to_delete.append({"dns_name": public.de_punycode(dns_name), "domain_dns_value": domain_dns_value}) responders.append({"dns_name":dns_name,"domain_dns_value":domain_dns_value,"authorization_url": authorization_url, "acme_keyauthorization": acme_keyauthorization,"dns_challenge_url": dns_challenge_url} ) @@ -498,33 +498,33 @@ class panelLets: try: for i in responders: - self.write_log(public.getMsg("CHECK_TXT_RECORD",(i['dns_name'],i['domain_dns_value']))) + self.write_log(public.get_msg_gettext('|-Attempt to verify the resolution result, domain name [{}], record value [{}]...',(i['dns_name'],i['domain_dns_value']))) self.check_dns(self.get_acme_name(i['dns_name']),i['domain_dns_value']) - self.write_log(public.getMsg("CA_CHECK_RECORD",(i['dns_name']))) + self.write_log(public.get_msg_gettext('|-Request CA to verify domain name [{}]...',(i['dns_name']))) auth_status_response = client.check_authorization_status(i["authorization_url"]) r_data = auth_status_response.json() if r_data["status"] == "pending": client.respond_to_challenge(i["acme_keyauthorization"], i["dns_challenge_url"]) for i in responders: - self.write_log(public.getMsg("CHECK_CA_RES",(i['dns_name'],))) + self.write_log(public.get_msg_gettext('|-Check CA verification results [{}]...',(i['dns_name'],))) client.check_authorization_status(i["authorization_url"], ["valid","invalid"]) except Exception as ex: - self.write_log(public.getMsg("APPLY_WITH_DNS_ERR",(str(ex),))) + self.write_log(public.get_msg_gettext('|-An error occurred, try again [{}]',(str(ex),))) for i in responders: - self.write_log(public.getMsg("CHECK_TXT_RECORD",(i['dns_name'],i['domain_dns_value']))) + self.write_log(public.get_msg_gettext('|-Attempt to verify the resolution result, domain name [{}], record value [{}]...',(i['dns_name'],i['domain_dns_value']))) self.check_dns(self.get_acme_name(i['dns_name']),i['domain_dns_value']) - self.write_log(public.getMsg("CA_CHECK_RECORD",(i['dns_name']))) + self.write_log(public.get_msg_gettext('|-Request CA to verify domain name [{}]...',(i['dns_name']))) auth_status_response = client.check_authorization_status(i["authorization_url"]) r_data = auth_status_response.json() if r_data["status"] == "pending": client.respond_to_challenge(i["acme_keyauthorization"], i["dns_challenge_url"]) for i in responders: - self.write_log(public.getMsg("CHECK_CA_RES",(i['dns_name'],))) + self.write_log(public.get_msg_gettext('|-Check CA verification results [{}]...',(i['dns_name'],))) client.check_authorization_status(i["authorization_url"], ["valid","invalid"]) - self.write_log(public.getMsg("ALL_DOMAIN_V_PASS")) + self.write_log(public.get_msg_gettext('|-All domain names are verified and CSR is being sent...')) certificate_url = client.send_csr(finalize_url) - self.write_log(public.getMsg("FETCH_CERT_CONTENT")) + self.write_log(public.get_msg_gettext('|-Fetching certificate content...')) certificate = client.download_certificate(certificate_url) if certificate: certificate = self.split_ca_data(certificate) @@ -539,7 +539,7 @@ class panelLets: finally: try: for i in dns_names_to_delete: - self.write_log(public.getMsg("CLEAR_RESOLVE_HISTORY",(i["dns_name"]))) + self.write_log(public.get_msg_gettext('|-Clearing resolve history [{}]',(i["dns_name"]))) dns_class.delete_dns_record(i["dns_name"], i["domain_dns_value"]) except : pass @@ -547,10 +547,10 @@ class panelLets: except Exception as e: try: for i in dns_names_to_delete: - self.write_log(public.getMsg("CLEAR_RESOLVE_HISTORY",(i["dns_name"]))) + self.write_log(public.get_msg_gettext('|-Clearing resolve history [{}]',(i["dns_name"]))) dns_class.delete_dns_record(i["dns_name"], i["domain_dns_value"]) except:pass - self.write_log(public.getMsg("DNS_APPLY_ERR",(str(public.get_error_info()),))) + self.write_log(public.get_msg_gettext('|-Error: {}, exit the application process.',(str(public.get_error_info()),))) self.write_log("=" * 50) res = str(e).split('>>>>') err = False @@ -566,17 +566,17 @@ class panelLets: result['status'] = False result['clecks'] = [] try: - self.write_log(public.getMsg("INIT_ACME")) + self.write_log(public.get_msg_gettext('|-Initializing ACME protocol...')) log_level = "INFO" if data['account_key']: log_level = 'ERROR' if not data['email']: data['email'] = public.M('users').getField('email') client = sewer.Client(domain_name = data['first_domain'],dns_class = None,account_key = data['account_key'],domain_alt_names = data['domains'],contact_email = str(data['email']),LOG_LEVEL = log_level,ACME_AUTH_STATUS_WAIT_PERIOD = 15,ACME_AUTH_STATUS_MAX_CHECKS = 5,ACME_REQUEST_TIMEOUT = 20,ACME_DIRECTORY_URL = self.let_url) - self.write_log(public.getMsg("REGISTER_ACCOUNT")) + self.write_log(public.get_msg_gettext('|-Registering account...')) client.acme_register() authorizations, finalize_url = client.apply_for_cert_issuance() responders = [] sucess_domains = [] - self.write_log(public.getMsg("GET_VERIFICATION_INFO")) + self.write_log(public.get_msg_gettext('|-Getting verification information...')) for url in authorizations: identifier_auth = self.get_identifier_authorization(client,url) @@ -591,21 +591,21 @@ class panelLets: #写入token wellknown_path = acme_dir + '/' + http_token - self.write_log(public.getMsg("CREATE_V_FILE",(wellknown_path,))) + self.write_log(public.get_msg_gettext('|-Writing verification file [{}]...',(wellknown_path,))) public.writeFile(wellknown_path,acme_keyauthorization) - wellknown_url = "http://{0}/.well-known/acme-challenge/{1}".format(http_name, http_token) + wellknown_url = "http://{}/.well-known/acme-challenge/{}".format(http_name, http_token) result['clecks'].append({'wellknown_url':wellknown_url,'http_token':http_token}) is_check = False n = 0 - self.write_log(public.getMsg("CHECK_FILE_CONTENT",(wellknown_url))) + self.write_log(public.get_msg_gettext('|-Attempt to verify file contents via HTTP [{}]...',(wellknown_url))) while n < 5: print("wait_check_authorization_status") try: retkey = public.httpGet(wellknown_url,20) if retkey == acme_keyauthorization: is_check = True - self.write_log(public.getMsg("CHECK_FILE_CONTENT1",(retkey,))) + self.write_log(public.get_msg_gettext('|-Verified, content [{}]...',(retkey,))) break except : pass @@ -617,18 +617,18 @@ class panelLets: if len(sucess_domains) > 0: #验证 for i in responders: - self.write_log(public.getMsg("CA_CHECK_RECORD",(i['http_name'],))) + self.write_log(public.get_msg_gettext('|-Request CA to verify domain name [{}]...',(i['http_name'],))) auth_status_response = client.check_authorization_status(i["authorization_url"]) if auth_status_response.json()["status"] == "pending": client.respond_to_challenge(i["acme_keyauthorization"], i["http_challenge_url"]).json() for i in responders: - self.write_log(public.getMsg("CHECK_CA_RES",(i['http_name'],))) + self.write_log(public.get_msg_gettext('|-Check CA verification results [{}]...',(i['http_name'],))) client.check_authorization_status(i["authorization_url"], ["valid","invalid"]) - self.write_log(public.getMsg("ALL_DOMAIN_V_PASS")) + self.write_log(public.get_msg_gettext('|-All domain names are verified and CSR is being sent...')) certificate_url = client.send_csr(finalize_url) - self.write_log(public.getMsg("GET_CERT_CONTENT")) + self.write_log(public.get_msg_gettext('|-Getting certificate content...')) certificate = client.download_certificate(certificate_url) if certificate: @@ -640,11 +640,11 @@ class panelLets: result['status'] = True else: - result['msg'] = public.getMsg('CERT_APPLY_ERR') + result['msg'] = public.get_msg_gettext('Certificate acquisition failed, please try again later.') else: - result['msg'] = public.getMsg("APPLY_SSL_ERROR_MSG") + result['msg'] = public.get_msg_gettext('The signing failed, we were unable to verify your domain name:

                                      1. Check if the domain name is bound to the corresponding site.

                                      2. Check if the domain name is correctly resolved to the server, or the resolution is not fully effective.

                                      3. If your site has a reverse proxy set up, or if you are using a CDN, please turn it off first.

                                      4. If your site has a 301 redirect, please turn it off first

                                      5. If the above checks confirm that there is no problem, please try to change the DNS service provider.

                                      ') except Exception as e: - self.write_log(public.getMsg("DNS_APPLY_ERR",(str(public.get_error_info()),))) + self.write_log(public.get_msg_gettext('|-Error: {}, exit the application process.',(str(public.get_error_info()),))) self.write_log("=" * 50) res = str(e).split('>>>>') err = False @@ -693,7 +693,7 @@ class panelLets: for i in j.items: txt_value = i.to_text().replace('"','').strip() if txt_value == value: - self.write_log(public.getMsg("SUCCESS_V",(domain,type,txt_value))) + self.write_log(public.get_msg_gettext('|-Successful verification, domain name [{}], record type [{}], record value [{}]!',(domain,type,txt_value))) print("Verification succeeded: %s" % txt_value) return True except: @@ -753,18 +753,18 @@ class panelLets: def renew_lets_ssl(self): cpath = self.setupPath + '/panel/vhost/cert/crontab.json' if not os.path.exists(cpath): - print(public.getMsg("NO_ORDER_RENEW") ) + print(public.get_msg_gettext('|-There are currently no certificates to renew.') ) else: old_list = json.loads(public.ReadFile(cpath)) print('=======================================================================') - print(public.getMsg('TOTAL_RENEW',(time.strftime('%Y-%m-%d %X',time.localtime()),str(len(old_list))))) + print(public.get_msg_gettext('|-{} Total [{}] renewal of visa tasks',(time.strftime('%Y-%m-%d %X',time.localtime()),str(len(old_list))))) cron_list = self.get_renew_lets_bytimeout(old_list) tlist = [] for siteName in old_list: if not siteName in cron_list: tlist.append(siteName) - print(public.getMsg('SSL_NOT_EXPIRED_OR_NOT_USE',(','.join(tlist),))) - print(public.getMsg('WAIT_RENEW1',(time.strftime('%Y-%m-%d %X',time.localtime()),str(len(cron_list))))) + print(public.get_msg_gettext('|-[{}] Not expired or the site does not use the Let\s Encrypt certificate.',(','.join(tlist),))) + print(public.get_msg_gettext('|-{} Waiting for renewal [{}].',(time.strftime('%Y-%m-%d %X',time.localtime()),str(len(cron_list))))) sucess_list = [] err_list = [] @@ -775,11 +775,11 @@ class panelLets: sucess_list.append(siteName) else: err_list.append({"siteName":siteName,"msg":ret['msg']}) - print(public.getMsg("RENEW_COMPLETED",(str(len(cron_list)),str(len(sucess_list)),str(len(err_list))))) + print(public.get_msg_gettext('|-After the task is completed, a total of renewals are required.[{}], renewal success [%s], renewal failed [{}]. ',(str(len(cron_list)),str(len(sucess_list)),str(len(err_list))))) if len(sucess_list) > 0: - print(public.getMsg("RENEW_SUCCESS2",(','.join(sucess_list),))) + print(public.get_msg_gettext('|-Renewal success:{}',(','.join(sucess_list),))) if len(err_list) > 0: - print(public.getMsg("RENEW_FAILED2")) + print(public.get_msg_gettext('|-Renewal failed:')) for x in err_list: print(" %s ->> %s" % (x['siteName'],x['msg'])) diff --git a/class/panelMessage.py b/class/panelMessage.py index 4fb7f4d4..c321f713 100644 --- a/class/panelMessage.py +++ b/class/panelMessage.py @@ -44,33 +44,36 @@ class panelMessage: """ - 获取官网推送消息,一小时获取一次 + 获取官网推送消息,一天获取一次 """ def get_cloud_messages(self,args): - #ret = cache.get('get_cloud_messages') - #if ret: return public.returnMsg(True,'同步成功1!') - data = {} - data['version'] = public.version() - data['os'] = self.os - sUrl = public.GetConfigValue('home') + '/api/wpanel/get_messages' - import http_requests - http_requests.DEFAULT_TYPE = 'src' - info = http_requests.post(sUrl,data).json() - # info = json.loads(public.httpPost(sUrl,data)) - for x in info: - count = public.M('messages').where('level=? and msg=?',(x['level'],x['msg'],)).count() - if count: continue + try: + ret = cache.get('get_cloud_messages') + if ret: return public.returnMsg(True,'同步成功1!') + data = {} + data['version'] = public.version() + data['os'] = self.os + sUrl = public.GetConfigValue('home') + '/api/wpanel/get_messages' + import http_requests + http_requests.DEFAULT_TYPE = 'src' + info = http_requests.post(sUrl,data).json() + # info = json.loads(public.httpPost(sUrl,data)) + for x in info: + count = public.M('messages').where('level=? and msg=?',(x['level'],x['msg'],)).count() + if count: continue - pdata = { - "level":x['level'], - "msg":x['msg'], - "state":1, - "expire":int(time.time()) + (int(x['expire']) * 86400), - "addtime": int(time.time()) - } - public.M('messages').insert(pdata) - #cache.set('get_cloud_messages',3600) - return public.returnMsg(True,'同步成功!') + pdata = { + "level":x['level'], + "msg":x['msg'], + "state":1, + "expire":int(time.time()) + (int(x['expire']) * 86400), + "addtime": int(time.time()) + } + public.M('messages').insert(pdata) + cache.set('get_cloud_messages',86400) + return public.returnMsg(True,'同步成功!') + except: + return public.returnMsg(False,'同步失败!') def get_messages(self,args = None): ''' @@ -78,7 +81,7 @@ class panelMessage: @author hwliang <2020-05-18> @return list ''' - self.get_cloud_messages(args) + public.run_thread(self.get_cloud_messages,args=(args,)) data = public.M('messages').where('state=? and expire>?',(1,int(time.time()))).order("id desc").select() return data @@ -88,7 +91,7 @@ class panelMessage: @author hwliang <2020-05-18> @return list ''' - self.get_cloud_messages(args) + public.run_thread(self.get_cloud_messages,args=(args,)) data = public.M('messages').order("id desc").select() return data diff --git a/class/panelMysql.py b/class/panelMysql.py index 9186caf4..bfb23517 100644 --- a/class/panelMysql.py +++ b/class/panelMysql.py @@ -22,7 +22,13 @@ class panelMysql: def __Conn(self): if self.__DB_NET: return True try: - socket = '/tmp/mysql.sock' + myconf = public.readFile('/etc/my.cnf') + socket_re = re.search(r"socket\s*=\s*(.+)",myconf) + if socket_re: + socket = socket_re.groups()[0] + else: + socket = '/tmp/mysql.sock' + try: if sys.version_info[0] != 2: try: @@ -34,7 +40,7 @@ class panelMysql: import MySQLdb if sys.version_info[0] == 2: reload(MySQLdb) - except Exception as ex: + except: try: import pymysql pymysql.install_as_MySQLdb() @@ -43,7 +49,7 @@ class panelMysql: self.__DB_ERR = e return False try: - myconf = public.readFile('/etc/my.cnf') + rep = r"port\s*=\s*([0-9]+)" self.__DB_PORT = int(re.search(rep,myconf).groups()[0]) except: @@ -65,12 +71,34 @@ class panelMysql: def connect_network(self,host,port,username,password): self.__DB_NET = True try: + try: + if sys.version_info[0] != 2: + try: + import pymysql + except: + public.ExecShell("pip install pymysql") + import pymysql + pymysql.install_as_MySQLdb() + import MySQLdb + if sys.version_info[0] == 2: + reload(MySQLdb) + except: + try: + import pymysql + pymysql.install_as_MySQLdb() + import MySQLdb + except Exception as e: + self.__DB_ERR = e + return False self.__DB_CONN = MySQLdb.connect(host = host,user = username,passwd = password,port = port,charset="utf8",connect_timeout=10) self.__DB_CUR = self.__DB_CONN.cursor() + return True except MySQLdb.Error as e: self.__DB_ERR = e return False + + def execute(self,sql): #执行SQL语句返回受影响行 if not self.__Conn(): return self.__DB_ERR @@ -99,6 +127,7 @@ class panelMysql: except Exception as ex: return ex + #关闭连接 def __Close(self): self.__DB_CUR.close() diff --git a/class/panelPHP.py b/class/panelPHP.py index 5379b05e..48a56dfe 100644 --- a/class/panelPHP.py +++ b/class/panelPHP.py @@ -61,15 +61,8 @@ class panelPHP: data = {} data['GET'] = request.args.to_dict() data['POST'] = {} - x_token = request.headers.get('x-http-token') - if x_token: - aes_pwd = x_token[:8] + x_token[40:48] for key in request.form.keys(): data['POST'][key] = str(request.form.get(key,'')) - if x_token: - if len(data['POST'][key]) > 5: - if data['POST'][key][:6] == 'BT-CRT': - data['POST'][key] = public.aes_decrypt(data['POST'][key][6:],aes_pwd) data['POST']['client_ip'] = public.GetClientIp() data = json.dumps(data) public.writeFile(self.__args_tmp,data) @@ -93,7 +86,7 @@ class panelPHP: php_vs = json.loads(public.readFile(php_v_file).replace('.','')) else: #否则兼容所有版本 - php_vs = ["80","74","73","72","71","70","56","55","54","53","52"] + php_vs = public.get_php_versions(True) #判段兼容的PHP版本是否安装 php_path = "/www/server/php/" php_v = None @@ -125,7 +118,7 @@ class panelPHP: else: php_vs = sorted(php_version,reverse=True) else: - php_vs = ["80","74","73","72","71","70","56","55","54","53","52"] + php_vs = public.get_php_versions(True) php_path = "/www/server/php/" php_v = None for pv in php_vs: @@ -601,7 +594,7 @@ class FPM(object): 'DOCUMENT_ROOT': self.document_root, 'SERVER_PROTOCOL' : 'HTTP/1.1', 'REMOTE_ADDR': '127.0.0.1', - 'REMOTE_PORT': '7800', + 'REMOTE_PORT': '8888', 'SERVER_ADDR': '127.0.0.1', 'SERVER_PORT': '80', 'SERVER_NAME': 'BT-Panel' diff --git a/class/panelPlugin.py b/class/panelPlugin.py index 997220ea..17057585 100644 --- a/class/panelPlugin.py +++ b/class/panelPlugin.py @@ -2,7 +2,7 @@ #------------------------------------------------------------------- # 宝塔Linux面板 #------------------------------------------------------------------- -# Copyright (c) 2015-2019 宝塔软件(http:#bt.cn) All rights reserved. +# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved. #------------------------------------------------------------------- # Author: hwliang #------------------------------------------------------------------- @@ -21,6 +21,7 @@ class panelPlugin: __product_list = None __plugin_list = None __exists_names = {} + __plugin_s_list = [] __official_url = 'https://brandnew.aapanel.com' pids = None ROWS = 15 @@ -130,12 +131,12 @@ class panelPlugin: p_node = '/www/server/panel/install/public.sh' if os.path.exists(p_node): if len(public.readFile(p_node)) < 100: os.remove(p_node) - if not pluginInfo: return public.returnMsg(False, 'INIT_PLUGIN_NOT_EXISTS') + if not pluginInfo: return public.return_msg_gettext(False, 'The specified plugin does not exist!') self.mutex_title = pluginInfo['mutex'] - if not self.check_mutex(pluginInfo['mutex']): return public.returnMsg(False, 'UNINSTALL_FIRST', + if not self.check_mutex(pluginInfo['mutex']): return public.return_msg_gettext(False, 'Please uninstall [{}] first', (self.mutex_title,)) if not hasattr(get, 'id'): - if not self.check_dependnet(pluginInfo['dependent']): return public.returnMsg(False, 'DEP_PAGE', + if not self.check_dependnet(pluginInfo['dependent']): return public.return_msg_gettext(False, 'Depends on the following software, please install [{}] first', (pluginInfo['dependent'],)) if 'version' in get: for versionInfo in pluginInfo['versions']: @@ -144,24 +145,26 @@ class panelPlugin: if int(get.type) > 4: get.type = '0' if get.type == '0': if not self.check_cpu_limit(versionInfo['cpu_limit']): - return public.returnMsg(False,'At least [{0}] CPU cores are required to install'.format(versionInfo['cpu_limit'])) + return public.return_msg_gettext(False,'At least [{0}] CPU cores are required to install'.format(versionInfo['cpu_limit'])) if not self.check_mem_limit(versionInfo['mem_limit']): - return public.returnMsg(False,'At least [{0} MB] memory is required to install'.format(versionInfo['mem_limit'])) + return public.return_msg_gettext(False,'At least [{0} MB] memory is required to install'.format(versionInfo['mem_limit'])) if not self.check_os_limit(versionInfo['os_limit']): m_ps = {0: "All", 1: "Centos", 2: "Ubuntu/Debian"} - return public.returnMsg(False, 'Only supports [%s] system' % m_ps[int(versionInfo['os_limit'])]) + return public.return_msg_gettext(False, 'Only supports [{}] system',(m_ps[int(versionInfo['os_limit'])],)) if not hasattr(get, 'id'): - if not self.check_dependnet(versionInfo['dependent']): return public.returnMsg(False, - 'Depend on the following software, please install first [%s]' % - versionInfo[ - 'dependent']) + if not self.check_dependnet(versionInfo['dependent']): return public.return_msg_gettext(False,'Depend on the following software, please install first [{}]',(versionInfo['dependent'],)) #安装插件 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!') + str1 = public.get_msg_gettext("System critical directory is not writable!") + str2 = public.get_msg_gettext("1. If [System Hardening] is installed, please turn it off") + str3 = public.get_msg_gettext("2. If Yunsuo is installed, please turn off [System Hardening] feature") + str4 = public.get_msg_gettext("3. If Safedog is installed, please turn off [System Protection] feature") + str5 = public.get_msg_gettext("4. If other security software is used, please uninstall it") + if not self.check_sys_write(): return public.return_msg_gettext(False,'ERROR:{}
                                      {}

                                      {}
                                      {}

                                      '.format(str1,str2,str3,str4)) + if not 'sName' in get: return public.return_msg_gettext(False,'Please specify the software name!') #处理ols还不支持php81的情况 - if get.sName == "php-8.1" and public.get_webserver() == 'openlitespeed': - return public.returnMsg(False, 'Sorry, currently OLS official does not support php8.1') + # if get.sName == "php-8.1" and public.get_webserver() == 'openlitespeed': + # return public.return_msg_gettext(False, 'Sorry, currently OLS official does not support php8.1') pluginInfo = self.get_soft_find(get.sName) get.pluginInfo = pluginInfo check_result = self.check_install_limit(get) @@ -200,7 +203,7 @@ class panelPlugin: pluginInfo['versions'][0]['download'], token ),toFile) - if public.FileMd5(toFile) != pluginInfo['versions'][0]['md5']: return public.returnMsg(False,'CHECK_FILE_HASH') + if public.FileMd5(toFile) != pluginInfo['versions'][0]['md5']: return public.return_msg_gettext(False,'File hash verification failed, stop installation!') update = False if os.path.exists(pluginInfo['install_checks']): update =pluginInfo['versions'][0]['version_msg'] return self.update_zip(None,toFile,update) @@ -211,10 +214,10 @@ class panelPlugin: self.set_pyenv(toFile) public.ExecShell('/bin/bash ' + toFile + ' install &> /tmp/panelShell.pl') if os.path.exists(pluginInfo['install_checks']): - public.WriteLog('TYPE_SETUP','PLUGIN_INSTALL_LIB',(pluginInfo['title'],)) + public.write_log_gettext('Installer','Successfully installed plugin [{}]',(pluginInfo['title'],)) if os.path.exists(toFile): os.remove(toFile) - return public.returnMsg(True,'PLUGIN_INSTALL_SUCCESS') - return public.returnMsg(False,'INSTALL_FAIL') + return public.return_msg_gettext(True,'Installation succeeded!') + return public.return_msg_gettext(False,'Installation failed') # 设置Python环境变量 def set_pyenv(self, filename): @@ -232,14 +235,14 @@ class panelPlugin: #异步安装 def install_async(self,pluginInfo,get): mtype = 'install'; - mmsg = public.GetMsg("INSTALL") + mmsg = public.get_msg_gettext('Install') if hasattr(get, 'upgrade'): mtype = 'update' mmsg = 'upgrade' if not 'type' in get: get.type = '0' if int(get.type) > 4: get.type = '0' if get.sName == 'nginx': - if get.version == '1.8': return public.returnMsg(False,'NOT_SUP_NG1.8') + if get.version == '1.8': return public.return_msg_gettext(False,'Nginx 1.8.1 is too old, no longer available, please choose another version!') if get.sName.find('php-') != -1:get.sName = get.sName.split('-')[0] ols_execstr = "" if "php" == get.sName and os.path.exists('/usr/local/lsws/bin/lswsctrl'): @@ -250,7 +253,7 @@ class panelPlugin: if not os.path.exists(php_path): os.makedirs(php_path) apacheVersion='false' if public.get_webserver() == 'apache': - apacheVersion = public.readFile('/www/server/apache/version.pl') + apacheVersion = public.xss_version(public.readFile('/www/server/apache/version.pl')) public.writeFile('/var/bt_apacheVersion.pl',apacheVersion) public.writeFile('/var/bt_setupPath.conf','/www') if os.path.exists('/usr/bin/apt-get'): @@ -263,20 +266,22 @@ class panelPlugin: execstr = "cd /www/server/panel/install && /bin/bash install_soft.sh {} {} {} {} {}".format( get.type, mtype, get.sName, get.version, ols_execstr) if get.sName == "phpmyadmin": - execstr += "&> /tmp/panelExec.log && sleep 1 && /usr/local/lsws/bin/lswsctrl restart" + execstr += "&> /tmp/panelExec.log" + if public.get_webserver() == 'openlitespeed': + execstr += " && sleep 1 && /usr/local/lsws/bin/lswsctrl restart" # 清理日志文件 if os.path.exists("/tmp/panelExec.log"): public.writeFile("/tmp/panelExec.log","") public.M('tasks').add('id,name,type,status,addtime,execstr',(None, mmsg + '['+get.sName+'-'+get.version+']','execshell','0',time.strftime('%Y-%m-%d %H:%M:%S'),execstr)) cache.delete('install_task') public.writeFile('/tmp/panelTask.pl','True') - public.WriteLog('TYPE_SETUP','PLUGIN_ADD',(get.sName,get.version)) - return public.returnMsg(True,'PLUGIN_INSTALL_ADD') + public.write_log_gettext('Installer','Successfully added intallation task [{}-{}]',(get.sName,get.version)) + return public.return_msg_gettext(True,'Installation task added to queue') #卸载插件 def uninstall_plugin(self,get): pluginInfo = self.get_soft_find(get.sName) - if not pluginInfo: return public.returnMsg(False,'INIT_PLUGIN_NOT_EXISTS') + if not pluginInfo: return public.return_msg_gettext(False,'The specified plugin does not exist!') if pluginInfo['type'] != 5: pluginPath = self.__install_path + '/' + pluginInfo['name'] if pluginInfo['type'] != 6: @@ -293,12 +298,12 @@ class panelPlugin: public.ExecShell('/bin/bash ' + pluginPath + '/install.sh uninstall') if os.path.exists(pluginPath): public.ExecShell('rm -rf ' + pluginPath) - public.WriteLog('TYPE_SETUP','PLUGIN_UNINSTALL_SOFT',(pluginInfo['title'],)) - return public.returnMsg(True,'PLUGIN_UNINSTALL') + public.write_log_gettext('Installer','Successfully uninstalled software [{}]',(pluginInfo['title'],)) + return public.return_msg_gettext(True,'Uninstallaton succeeded') else: if pluginInfo['name'] == 'mysql': - if public.M('databases').count() > 0: return public.returnMsg(False,"RM_DB_WARN") + if public.M('databases').where('db_type=?',0).count() > 0: return public.return_msg_gettext(False,'The database list is not empty. For your data security, please backup and delete the existing database.
                                      Forced uninstall command: rm -rf /www/server/mysql') if pluginInfo['name'] == 'nginx': import nginx nginx.nginx().del_all_log_format(get) @@ -312,42 +317,54 @@ class panelPlugin: get.sName = get.sName.split('-')[0] execstr = "cd /www/server/panel/install && /bin/bash install_soft.sh "+get.type+" uninstall " + get.sName.lower() + " "+ get.version.replace('.','') public.ExecShell(execstr) - public.WriteLog('TYPE_SETUP','PLUGIN_UNINSTALL',(get.sName,get.version)) - return public.returnMsg(True,"PLUGIN_UNINSTALL") + public.write_log_gettext('Installer','Successfully unintalled [{}-{}]',(get.sName,get.version)) + return public.return_msg_gettext(True,"Uninstallation succeeded") #从云端取列表 def get_cloud_list(self,get=None): + lcoalTmp = 'data/plugin.json' + softList = None + listTmp = public.readFile(lcoalTmp) + force_refresh = 0 try: - lcoalTmp = 'data/plugin.json' - softList = None - listTmp = public.readFile(lcoalTmp) - force_refresh = 0 - try: - if listTmp: softList = json.loads(listTmp) - if 'success' in softList and not softList['success']: - if os.path.exists(lcoalTmp): os.remove(lcoalTmp) - force_refresh = 1 - except: + if listTmp: softList = json.loads(listTmp) + if 'success' in softList and not softList['success']: if os.path.exists(lcoalTmp): os.remove(lcoalTmp) + force_refresh = 1 + except: + if os.path.exists(lcoalTmp): os.remove(lcoalTmp) - if 'init' in get: - if softList: - if 'success' not in softList: - return softList + if get and 'init' in get: + if softList: + if 'success' not in softList: + return softList - focre = 0 - if hasattr(get,'force'): focre = int(get.force) - if 'focre_cloud' in session: - if session['focre_cloud']: - focre = 1 - session['focre_cloud'] = False - - if not 'init_cloud' in session: + focre = 0 + if hasattr(get,'force'): focre = int(get.force) + if 'focre_cloud' in session: + if session['focre_cloud']: focre = 1 - session['init_cloud'] = True - if force_refresh == 1: - focre = 1 - if not softList or focre > 0: + session['focre_cloud'] = False + + if not 'init_cloud' in session: + focre = 1 + session['init_cloud'] = True + if force_refresh == 1: + focre = 1 + if not softList or focre > 0: + requesting_tag_key = "REQUESTING_SERVER_SOFTWARE_LIST" + try: + max_waiting_time = 120 + has_waiting = False + while cache.get(requesting_tag_key): + has_waiting = True + time.sleep(0.1) + + if has_waiting: + return self.get_cloud_list(get) + + cache.set(requesting_tag_key, True, timeout=max_waiting_time) + self.clean_panel_log() # cloudUrl = 'https://console.aapanel.com/api/panel/get_soft_list' cloudUrl = '{}/api/panel/getSoftList'.format(self.__official_url) @@ -359,11 +376,11 @@ class panelPlugin: if 'token' in pdata: url_headers = {"authorization": "bt {}".format(pdata['token'])} pdata['environment_info'] = json.dumps(public.fetch_env_info()) - listTmp = requests.post(cloudUrl, params=pdata, headers=url_headers,verify=False) - listTmp=listTmp.json() - if not listTmp: - listTmp = public.readFile(lcoalTmp) try: + listTmp = requests.post(cloudUrl, params=pdata, headers=url_headers,verify=False) + listTmp=listTmp.json() + if not listTmp: + listTmp = public.readFile(lcoalTmp) softList = listTmp except: pass if softList: public.writeFile(lcoalTmp,json.dumps(softList)) @@ -371,35 +388,37 @@ class panelPlugin: public.run_thread(self.getCloudPHPExt) # 专业版和企业版到期提醒,aaPanel目前没有先注释 # self.expire_msg(softList) - try: - public.writeFile("/tmp/" + cache.get('p_token'),str(softList['pro'])) - except:pass - sType = 0 - try: - if hasattr(get,'type'): sType = int(get['type']) - if hasattr(get,'query'): - if get.query: sType = 0 - except:pass - softList['list'] = self.get_local_plugin(softList['list']) - softList['list'] = self.get_types(softList['list'],sType) + except: + pass + finally: + cache.delete(requesting_tag_key) + try: + public.writeFile("/tmp/" + cache.get('p_token'),str(softList['pro'])) + except:pass + sType = 0 + try: + if hasattr(get,'type'): sType = int(get['type']) if hasattr(get,'query'): - if get.query: - get.query = get.query.lower() - tmpList = [] - for softInfo in softList['list']: - if softInfo['name'].lower().find(get.query) != -1 or \ - softInfo['title'].lower().find(get.query) != -1 or \ - softInfo['ps'].lower().find(get.query) != -1: - tmpList.append(softInfo) - softList['list'] = tmpList - for softInfo in softList['list']: - if 'uninsatll_checks' not in softInfo: - softInfo['uninsatll_checks'] = softInfo['uninstall_checks'] - if not softList['list']: - if os.path.exists(lcoalTmp): os.remove(lcoalTmp) - return softList - except: - pass + if get.query: sType = 0 + except:pass + softList['list'] = self.get_local_plugin(softList['list']) + softList['list'] = self.get_types(softList['list'],sType) + if hasattr(get,'query'): + if get.query: + get.query = get.query.lower() + tmpList = [] + for softInfo in softList['list']: + if softInfo['name'].lower().find(get.query) != -1 or \ + softInfo['title'].lower().find(get.query) != -1 or \ + softInfo['ps'].lower().find(get.query) != -1: + tmpList.append(softInfo) + softList['list'] = tmpList + for softInfo in softList['list']: + if 'uninsatll_checks' not in softInfo: + softInfo['uninsatll_checks'] = softInfo['uninstall_checks'] + # if not softList['list']: + # if os.path.exists(lcoalTmp): os.remove(lcoalTmp) + return softList #取提醒标记 def get_level_msg(self,level,s_time,endtime): @@ -479,14 +498,14 @@ class panelPlugin: pdata['ps'] = args.ps pdata['num'] = int(args.num) pdata['pid'] = int(args.pid) - if 1< pdata['num'] >5: return public.returnMsg(False,'Scoring range [1-5]') - if not pdata['pid']: return public.returnMsg(False,'The specified plugin does not exist!') + if 1< pdata['num'] >5: return public.return_msg_gettext(False,'Scoring range [1-5]') + if not pdata['pid']: return public.return_msg_gettext(False,'The specified plugin does not exist!') result = public.httpPost(public.GetConfigValue('home') + '/api/panel/plugin_score',pdata,10) result = json.loads(result) return result except: - return public.returnMsg(False,'Connection failure!') + return public.return_msg_gettext(False,'Connection failure!') #获取指定插件评分 def get_score(self,args): @@ -509,7 +528,7 @@ class panelPlugin: result = json.loads(result) return result except: - return public.returnMsg(False,'Connection failure!') + return public.return_msg_gettext(False,'Connection failure!') #清除多余面板日志 def clean_panel_log(self): @@ -652,7 +671,7 @@ class panelPlugin: # sType = [sType,8] newList = [] for sInfo in sList: - if sInfo['type'] in sType: newList.append(sInfo) + if int(sInfo['type']) in sType: newList.append(sInfo) return newList #检查权限 @@ -662,7 +681,7 @@ class panelPlugin: p_list = self.get_cloud_list(args) for p in p_list['list']: if p['name'] == get.name: - if p_list['pro'] < 0 and p['endtime'] < 0: return False + if int(p_list['pro']) < 0 and int(p['endtime']) < 0: return False break args.type = '10' @@ -670,7 +689,7 @@ class panelPlugin: for p in p_list['list']: if p['name'] == get.name: if not 'endtime' in p: continue - if p['endtime'] < 0: return False + if int(p['endtime']) < 0: return False break args.type = '12' @@ -679,17 +698,19 @@ class panelPlugin: if not p['type'] in [12,'12']: continue if p['name'] == get.name: if not 'endtime' in p: continue - if p_list['ltd'] < 1 and p['endtime'] < 1: return False + if int(p_list['ltd']) < 1 and int(p['endtime']) < 1: return False break return True #取软件列表 def get_soft_list(self,get = None): + print("get soft list normal.") softList = self.get_cloud_list(get) if not softList: get.force = 1 + print("get soft list force.") softList = self.get_cloud_list(get) - if not softList: return public.returnMsg(False,'GET_SOFTLIST_FAIL',"401") + if not softList: return public.return_msg_gettext(False,'Failed to get software list ({})',"401") softList['list'] = self.set_coexist(softList['list']) if not 'type' in get: get.type = '0' if get.type == '-1': @@ -711,7 +732,10 @@ class panelPlugin: if public.readFile(check_version_path).find('2.2') == 0: softList['apache22'] = True softList['apache24'] = False - + if os.path.exists('/www/server/nginx/conf/nginx.conf'): + import one_key_wp + one_key_wp.fast_cgi().set_nginx_conf() + public.ExecShell("/etc/init.d/nginx start") return softList #取首页软件列表 @@ -720,7 +744,7 @@ class panelPlugin: if not softList: get.force = 1 softList = self.get_cloud_list(get)['list'] - if not softList: return public.returnMsg(False,'GET_SOFTLIST_FAIL',"401") + if not softList: return public.return_msg_gettext(False,'Failed to get software list ({})',"401") softList = self.set_coexist(softList) if not os.path.exists(self.__index): public.writeFile(self.__index,'[]') indexList = json.loads(public.ReadFile(self.__index)) @@ -737,7 +761,7 @@ class panelPlugin: sName = get.sName if not os.path.exists(self.__index): public.writeFile(self.__index,'[]') indexList = json.loads(public.ReadFile(self.__index)) - if sName in indexList: return public.returnMsg(False,'DONT_ADD_AGAIN') + if sName in indexList: return public.return_msg_gettext(False,'Please do NOT repeat adding') if len(indexList) >= 12: softList = self.get_cloud_list(get)['list'] softList = self.set_coexist(softList) @@ -753,10 +777,10 @@ class panelPlugin: new_softInfo = self.check_status(softInfo) if not new_softInfo['setup']: indexList.remove(softInfo['name']) public.writeFile(self.__index,json.dumps(indexList)) - if len(indexList) >= 12: return public.returnMsg(False,'HP_DIS_MOST') + if len(indexList) >= 12: return public.return_msg_gettext(False,'Dashboard only display up to 12 software!') indexList.append(sName) public.writeFile(self.__index,json.dumps(indexList)) - return public.returnMsg(True,'ADD_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #删除首页 def remove_index(self,get): @@ -764,16 +788,16 @@ class panelPlugin: indexList = [] if not os.path.exists(self.__index): public.writeFile(self.__index,'[]') indexList = json.loads(public.ReadFile(self.__index)) - if not sName in indexList: return public.returnMsg(True,'DEL_SUCCESS') + if not sName in indexList: return public.return_msg_gettext(True,'Successfully deleted!') indexList.remove(sName) public.writeFile(self.__index,json.dumps(indexList)) - return public.returnMsg(True,'DEL_SUCCESS') + return public.return_msg_gettext(True,'Successfully deleted!') #设置排序 def sort_index(self,get): indexList = get.ssort.split('|') public.writeFile(self.__index,json.dumps(indexList)) - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #取快捷软件列表 def get_link_list(self,get=None): @@ -791,27 +815,27 @@ class panelPlugin: def add_link(self,get): sName = get.sName indexList = json.loads(public.ReadFile(self.__link)) - if sName in indexList: return public.returnMsg(False,'DONT_ADD_AGAIN') - if len(indexList) >= 5: return public.returnMsg(False,'BAR_DIS_MOST') + if sName in indexList: return public.return_msg_gettext(False,'Please do NOT repeat adding') + if len(indexList) >= 5: return public.return_msg_gettext(False,'Shortcut Bar only display up to 5 software!') indexList.append(sName) public.writeFile(self.__link,json.dumps(indexList)) - return public.returnMsg(True,'ADD_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #删除快捷栏 def remove_link(self,get): sName = get.sName indexList = [] indexList = json.loads(public.ReadFile(self.__link)) - if sName in indexList: return public.returnMsg(True,'DEL_SUCCESS') + if sName in indexList: return public.return_msg_gettext(True,'Successfully deleted!') indexList.remove(sName) public.writeFile(self.__link,json.dumps(indexList)) - return public.returnMsg(True,'DEL_SUCCESS') + return public.return_msg_gettext(True,'Successfully deleted!') #设置快捷栏排序 def sort_link(self,get): indexList = get.ssort.split('|') public.writeFile(self.__link,json.dumps(indexList)) - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') @@ -866,7 +890,8 @@ class panelPlugin: softInfo['status'] = True softInfo['versions'] = self.tips_version(softInfo['versions'],softInfo['version']) softInfo['admin'] = os.path.exists('/www/server/panel/plugin/' + softInfo['name']) - if len(softInfo['s_version']) > 3: + + if 's_version' in softInfo and len(softInfo['s_version']) > 3: pNames = softInfo['s_version'].split(',') for pName in pNames: if len(softInfo['manager_version']) > 5: @@ -884,8 +909,8 @@ class panelPlugin: if softInfo['id'] != 10000: self.get_icon(softInfo['name'],softInfo['min_image']) else: - if softInfo['id'] != 10000: - self.get_icon(softInfo['name']) + # if softInfo['id'] != 10000: + self.get_icon(softInfo['name']) if softInfo['name'].find('php-') != -1: v2= softInfo['versions'][0]['m_version'].replace('.','') @@ -934,27 +959,39 @@ class panelPlugin: #取phpmyadmin状态 def get_phpmyadmin_stat(self): - if public.get_webserver() == 'nginx': + webserver = public.get_webserver() + if webserver == 'nginx': filename = public.GetConfigValue('setup_path') + '/nginx/conf/nginx.conf' - elif public.get_webserver() == 'apache': + elif webserver == 'apache': filename = public.GetConfigValue('setup_path') + '/apache/conf/extra/httpd-vhosts.conf' else: filename = "/www/server/panel/vhost/openlitespeed/detail/phpmyadmin.conf" if not os.path.exists(filename): return False conf = public.readFile(filename) if not conf: return False - return conf.find('/www/server/stop') == -1 + is_start = conf.find('/www/server/stop') == -1 + if is_start: + if webserver == 'nginx': + is_start = conf.find('allow 127.0.0.1;') == -1 + elif webserver == 'apache': + is_start = conf.find('Allow from 127.0.0.1 ::1 localhost') == -1 + return is_start + #获取指定软件信息 def get_soft_find(self,get = None): - softList = self.get_cloud_list(get)['list'] - softList = self.set_coexist(softList) + if not self.__plugin_s_list: + softList = self.get_cloud_list(get)['list'] + self.__plugin_s_list = self.set_coexist(softList) + try: sName = get['sName'] except: sName = get - for softInfo in softList: - if softInfo['name'] == sName: + + for softInfo in self.__plugin_s_list: + + if softInfo['name'] == sName: if sName == 'phpmyadmin': from BTPanel import get_phpmyadmin_dir pmd = get_phpmyadmin_dir() @@ -979,12 +1016,12 @@ class panelPlugin: version = '' vFile1 = sInfo['uninsatll_checks'] + '/version_check.pl' vFile2 = sInfo['uninsatll_checks'] + '/info.json' - if os.path.exists(vFile1): - version = public.ReadFile(vFile1).strip() + if os.path.exists(vFile1): + version = public.xss_version(public.ReadFile(vFile1).strip()) if not version: os.remove(vFile1) - elif os.path.exists(vFile2): + elif os.path.exists(vFile2): v_tmp = public.ReadFile(vFile2).strip() - if v_tmp: + if v_tmp: try: version = json.loads(v_tmp)['versions'] except: public.ExecShell('rm -f ' + vFile2) @@ -1016,13 +1053,13 @@ class panelPlugin: else: vFile4 = sInfo['uninsatll_checks'] + '/version.pl' if os.path.exists(vFile4): - version = public.readFile(vFile4).strip() + version = public.xss_version(public.readFile(vFile4).strip()) if sInfo['name'] == 'mysql': vFile3 = sInfo['uninsatll_checks'] + '/version.pl' version_str = None if os.path.exists(vFile3): - version_str = public.readFile(vFile3) + version_str = public.xss_version(public.readFile(vFile3)) if version_str.find('AliSQL') != -1: version = 'AliSQL' if version == 'Linux' and version_str: version = version_str @@ -1067,15 +1104,21 @@ class panelPlugin: if datadir: pid_file = "{}/{}.pid".format(datadir,public.get_hostname()) if os.path.exists(pid_file): - pid = int(public.readFile(pid_file)) - status = public.pid_exists(pid) - if status: return status + try: + pid = int(public.readFile(pid_file)) + status = public.pid_exists(pid) + if status: return status + except: + return False if pname in ['php-fpm'] and exe: pid_file = exe.replace('sbin/php-fpm','/var/run/php-fpm.pid') if os.path.exists(pid_file): - pid = int(public.readFile(pid_file)) - return public.pid_exists(pid) + try: + pid = int(public.readFile(pid_file)) + return public.pid_exists(pid) + except: + return False if not self.pids: self.pids = psutil.pids() for pid in self.pids: @@ -1218,10 +1261,12 @@ class panelPlugin: def get_icon(self,name,downFile = None): iconFile = 'BTPanel/static/img/soft_ico/ico-' + name + '.png' if not os.path.exists(iconFile): - self.download_icon(name,iconFile,downFile) + public.run_thread(self.download_icon,(name,iconFile,downFile)) else: size = os.path.getsize(iconFile) - if size == 0: self.download_icon(name,iconFile,downFile) + if size == 0: + public.run_thread(self.download_icon,(name,iconFile,downFile)) + # self.download_icon(name,iconFile,downFile) #下载图标 def download_icon(self,name,iconFile,downFile): @@ -1232,9 +1277,9 @@ class panelPlugin: public.ExecShell(r"\cp -a -r " + srcIcon + " " + iconFile) else: if downFile: - public.ExecShell('wget -O ' + iconFile + ' ' + public.GetConfigValue('home') + downFile) + public.ExecShell('wget -O ' + iconFile + ' ' + public.GetConfigValue('home') + downFile + " &") else: - public.ExecShell('wget -O ' + iconFile + ' ' + public.get_url() + '/install/plugin/' + name + '/icon.png') + public.ExecShell('wget -O ' + iconFile + ' ' + public.get_url() + '/install/plugin/' + name + '/icon.png' + " &") cache.set(skey,1,86400) @@ -1314,10 +1359,10 @@ class panelPlugin: self.set_pyenv(toFile) public.ExecShell('/bin/bash ' + toFile + ' install') if self.checksSetup(pluginInfo['name'],pluginInfo['checks'],pluginInfo['versions'])[0]['status'] or os.path.exists(self.__install_path + '/' + get.name): - public.WriteLog('TYPE_SETUP','PLUGIN_INSTALL_LIB',(pluginInfo['title'],)) + public.write_log_gettext('Installer','Successfully installed plugin [{}]',(pluginInfo['title'],)) #public.ExecShell('rm -f ' + toFile); - return public.returnMsg(True,'PLUGIN_INSTALL_SUCCESS') - return public.returnMsg(False,'PLUGIN_INSTALL_ERR') + return public.return_msg_gettext(True,'Installation succeeded!') + return public.return_msg_gettext(False,'Installation failed!') else: import db,time path = '/www/server/php' @@ -1346,8 +1391,8 @@ class panelPlugin: id = None sql.table('tasks').add('id,name,type,status,addtime,execstr',(None, mmsg + '['+get.name+'-'+get.version+']','execshell','0',time.strftime('%Y-%m-%d %H:%M:%S'),execstr)) public.writeFile(isTask,'True') - public.WriteLog('TYPE_SETUP','PLUGIN_ADD',(get.name,get.version)) - return public.returnMsg(True,'PLUGIN_INSTALL') + public.write_log_gettext('Installer','Successfully added intallation task [{}-{}]',(get.name,get.version)) + return public.return_msg_gettext(True,'Installation task added to queue') #卸载插件 @@ -1375,8 +1420,8 @@ class panelPlugin: if os.path.exists(pluginPath): public.ExecShell('rm -rf ' + pluginPath) - public.WriteLog('TYPE_SETUP','PLUGIN_UNINSTALL_SOFT',(pluginInfo['title'],)) - return public.returnMsg(True,'PLUGIN_UNINSTALL') + public.write_log_gettext('Installer','Successfully uninstalled software [{}]',(pluginInfo['title'],)) + return public.return_msg_gettext(True,"Uninstallation succeeded") else: get.type = '0' issue = public.readFile('/etc/issue') @@ -1384,8 +1429,8 @@ class panelPlugin: public.writeFile('/var/bt_setupPath.conf',public.GetConfigValue('root_path')) execstr = "cd /www/server/panel/install && /bin/bash install_soft.sh "+get.type+" uninstall " + get.name.lower() + " "+ get.version.replace('.','') public.ExecShell(execstr) - public.WriteLog('TYPE_SETUP','PLUGIN_UNINSTALL',(get.name,get.version)) - return public.returnMsg(True,"PLUGIN_UNINSTALL") + public.WriteLog('TYPE_SETUP','Successfully uninstalled [{}-{}]',(get.name,get.version)) + return public.returnMsg(True,"Uninstallation succeeded") #取产品信息 def getProductInfo(self,productName): @@ -1403,15 +1448,15 @@ class panelPlugin: import panelAuth Auth = panelAuth.panelAuth() tmp = Auth.get_plugin_list(None) - if not tmp: return public.GetMsg("NOT_OPENED") - if not 'data' in tmp: return public.GetMsg("NOT_OPENED") + if not tmp: return public.get_msg_gettext('NOT opened') + if not 'data' in tmp: return public.get_msg_gettext('NOT opened') self.__plugin_list = tmp['data'] for pluinfo in self.__plugin_list: if pluinfo['product'] == pluginName: - if not pluinfo['endtime'] or not pluinfo['state']: return public.GetMsg("WAITING_FOR_PAY") - if pluinfo['endtime'] < time.time(): return public.GetMsg("EXPIRED") + if not pluinfo['endtime'] or not pluinfo['state']: return public.get_msg_gettext('To be paid') + if pluinfo['endtime'] < time.time(): return public.get_msg_gettext('Expired') return time.strftime("%Y-%m-%d",time.localtime(pluinfo['endtime'])); - return public.GetMsg("NOT_OPENED") + return public.get_msg_gettext('NOT opened') #取插件列表 def getPluginList(self,get): @@ -1426,7 +1471,7 @@ class panelPlugin: try: apavFile = '/www/server/apache/version.pl' if os.path.exists(apavFile): - apacheVersion = public.readFile(apavFile).strip() + apacheVersion = public.xss_version(public.readFile(apavFile).strip()) except: pass @@ -1491,7 +1536,7 @@ class panelPlugin: for n in range(l): if data[n]['pid'] == int(ssort[i]): data[n]['sort'] = i public.writeFile(self.__list,json.dumps(data)) - return public.returnMsg(True,'PLUGIN_SORT') + return public.return_msg_gettext(True,'Sort saved') #检查是否安装 def checksSetup(self,name,checks,vers = ''): @@ -1499,7 +1544,7 @@ class panelPlugin: versions = [] path = '/www/server/' + name + '/version.pl' v1 = '' - if os.path.exists(path): v1 = public.readFile(path).strip() + if os.path.exists(path): v1 = public.xss_version(public.readFile(path).strip()) if name == 'nginx': v1 = v1.replace('1.10', '1.12') if not self.__tasks: self.__tasks = public.M('tasks').where("status!=?",('1',)).field('status,name').select() @@ -1514,10 +1559,10 @@ class panelPlugin: for tm in tmp: if name == 'php': path = '/www/server/php/' + v2 - if os.path.exists(path + '/bin/php') and not os.path.exists(path + '/version.pl'): + if os.path.exists(path + '/bin/php') and not os.path.exists(path + '/version.pl'): public.ExecShell("echo `"+path+"/bin/php 2>/dev/null -v|grep cli|awk '{print $2}'` > " + path + '/version.pl') try: - v1 = public.readFile(path+'/version.pl').strip() + v1 = public.xss_version(public.readFile(path+'/version.pl').strip()) if not v1: public.ExecShell('rm -f ' + path + '/version.pl') except: v1 = "" @@ -1725,14 +1770,14 @@ class panelPlugin: try: vfile = setupPath + '/phpmyadmin/version.pl' if os.path.exists(vfile): - tmp['version'] = public.readFile(vfile).strip() + tmp['version'] = public.xss_version(public.readFile(vfile).strip()) tmp['status'] = True tmp['no'] = tmp['version'] else: tmp['version'] = "" tmp['status'] = False tmp['no'] = "" - + tmp['run'] = pstatus tmp['phpversion'] = phpversion tmp['port'] = phpport @@ -1818,16 +1863,25 @@ class panelPlugin: #获取配置模板 def getConfigHtml(self,get): filename = self.__install_path + '/' + get.name + '/index.html' - if not os.path.exists(filename): return public.returnMsg(False,'PLUGIN_GET_HTML') + if not os.path.exists(filename): return public.return_msg_gettext(False,'This plugin does NOT have template!') mimetype = 'text/html' cache_time = 0 if public.is_debug() else 86400 self.plugin_open_total(get.name) - return send_file(filename, - mimetype = mimetype, - as_attachment = True, - add_etags = True, - conditional = True, - cache_timeout = cache_time) + import flask + if flask.__version__ < "2.1.0": + return send_file(filename, + mimetype = mimetype, + as_attachment = True, + add_etags = True, + conditional = True, + cache_timeout = cache_time) + else: + return send_file(filename, + mimetype = mimetype, + as_attachment = True, + etag = True, + conditional = True, + max_age = cache_time) def creatab_open_total_table(self,sql): @@ -1905,7 +1959,7 @@ class panelPlugin: return [] else: return self.__get_plugin_upgrades(plugin_name) - + #取插件信息 def getPluginInfo(self,get): try: @@ -1914,7 +1968,7 @@ class panelPlugin: try: apavFile = '/www/server/apache/version.pl' if os.path.exists(apavFile): - apacheVersion = public.readFile(apavFile).strip() + apacheVersion = public.xss_version(public.readFile(apavFile).strip()) except: pass if pluginInfo['name'] == 'php': @@ -1922,14 +1976,14 @@ class panelPlugin: pluginInfo['versions'] = '5.2,5.3,5.4' elif apacheVersion == '2.4': pluginInfo['versions'] = '5.3,5.4,5.5,5.6,7.0,7.1,7.2,7.3,7.4' - + pluginInfo['versions'] = self.checksSetup(pluginInfo['name'],pluginInfo['checks'],pluginInfo['versions']) if get.name == 'php': pluginInfo['phpSort'] = public.readFile('/www/server/php/sort.pl') return pluginInfo except: return False - + #取插件状态 def getPluginStatus(self,get): find = self.GetFind(get.name) @@ -1944,7 +1998,7 @@ class panelPlugin: tmp['status'] = find['status'] versions.append(tmp) return versions - + #设置插件状态 def setPluginStatus(self,get): if get.name == 'php': @@ -1959,18 +2013,18 @@ class panelPlugin: break else: public.writeFile(path + '/' + get.version.replace('.','') + '/display.pl','True') - + if isRemove: self.SetField(get.name, 'display', int(get.status)) else: self.SetField(get.name, 'display', int(get.status)) - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #从云端获取插件列表 def getCloudPlugin(self,get): - if session.get('getCloudPlugin') and get != None: return public.returnMsg(True,'PLUGIN_UPDATE_ERR1',("-1",)) + if session.get('getCloudPlugin') and get != None: return public.return_msg_gettext(True,'Your plugin list is already the latest version {}!',("-1",)) import json - if not session.get('download_url'): session['download_url'] = 'https://node.aapanel.com' + if not session.get('download_url'): session['download_url'] = 'http://node.aapanel.com' #获取列表 try: @@ -1984,21 +2038,21 @@ class panelPlugin: except: download_url = session['download_url'] + '/install/list_pro.json' data = json.loads(public.httpGet(download_url)) - + n = i = j = 0 - + lists = self.GetList(None) - + for i in range(len(data)): for pinfo in lists: if data[i]['name'] != pinfo['name']: continue data[i]['display'] = pinfo['display'] - if data[i]['default']: + if data[i]['default']: get.name = data[i]['name'] self.install(get) public.writeFile(self.__list,json.dumps(data)) - + #获取分类 try: download_url = session['download_url'] + '/install/type.json' @@ -2006,16 +2060,16 @@ class panelPlugin: public.writeFile(self.__type,json.dumps(types)) except: pass - + self.getCloudPHPExt(get) self.GetCloudWarning(get) session['getCloudPlugin'] = True - return public.returnMsg(True,'PLUGIN_UPDATE') + return public.return_msg_gettext(True,'Software list updated!') #刷新缓存 def flush_cache(self,get): self.getCloudPlugin(None) - return public.returnMsg(True,'SOFTLIST_UPDATE_SUCCESS') + return public.return_msg_gettext(True,'Software list updated!') #获取PHP扩展 def getCloudPHPExt(self,get=None): @@ -2085,30 +2139,30 @@ class panelPlugin: #请求插件事件 def a(self,get): - if not hasattr(get,'name'): return public.returnMsg(False,'PLUGIN_INPUT_A') + if not hasattr(get,'name'): return public.return_msg_gettext(False,'Input name of plugin!') try: - if not public.path_safe_check("%s/%s" % (get.name,get.s)): return public.returnMsg(False,'PLUGIN_INPUT_C') + if not public.path_safe_check("%s/%s" % (get.name,get.s)): return public.return_msg_gettext(False,'Requested method [{}] does not exist!') path = self.__install_path + '/' + get.name 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,"You did not purchase [ %s ] or the authorization has expired" % (self.get_title_byname(get),)) + return public.return_msg_gettext(False,'This plugin does NOT have extend function!') + if not self.check_accept(get):return public.return_msg_gettext(False,"You did not purchase [ {} ] or the authorization has expired", (self.get_title_byname(get),)) public.package_path_append(path) plugin_main = __import__(get.name+'_main') try: reload(plugin_main) except: pass pluginObject = eval('plugin_main.' + get.name + '_main()') - if not hasattr(pluginObject,get.s): return public.returnMsg(False,'PLUGIN_INPUT_C',(get.s,)) + if not hasattr(pluginObject,get.s): return public.return_msg_gettext(False,'Requested method [{}] does not exist!',(get.s,)) execStr = 'pluginObject.' + get.s + '(get)' return eval(execStr) except: import traceback errorMsg = traceback.format_exc() public.writeFile('logs/done.log',errorMsg) - return public.returnMsg(False,'%s:
                                      %s ' % (public.GetMsg("HAVE_ERR"),errorMsg.replace('\n','
                                      '))) + return public.return_msg_gettext(False,'%s:
                                      %s ' % (public.get_msg_gettext('Sorry, something went wrong'),errorMsg.replace('\n','
                                      '))) #上传插件包 def update_zip(self,get = None,tmp_file = None, update = False): tmp_path = '/www/server/panel/temp' @@ -2116,7 +2170,7 @@ class panelPlugin: os.makedirs(tmp_path,mode=384) if tmp_file: - if not os.path.exists(tmp_file): return public.returnMsg(False,'DOWNLOAD_FILE_FAIL') + if not os.path.exists(tmp_file): return public.return_msg_gettext(False,'File download failed!') if get: @@ -2151,7 +2205,7 @@ class panelPlugin: except: data = json.loads(public.ReadFile(p_info).decode('utf-8-sig')) data['size'] = public.get_path_size(tmp_path) - if not 'author' in data: data['author'] = public.GetMsg("UNKNOW") + if not 'author' in data: data['author'] = public.get_msg_gettext('Unknown') if not 'home' in data: data['home'] = 'https://www.bt.cn/bbs/forum-40-1.html' plugin_path = '/www/server/panel/plugin/' + data['name'] + '/info.json' @@ -2164,14 +2218,14 @@ class panelPlugin: except:pass except: public.ExecShell("rm -rf " + tmp_path + '/*') - return public.returnMsg(False,'CANT_FIND_PLU_IN_PACKAGE') + return public.return_msg_gettext(False,'No plugin info found in the archive, please check the plugin package!') data['update'] = update return data #导入插件包 def input_zip(self,get): - if not os.path.exists(get.tmp_path): return public.returnMsg(False,'TEM_FILE_NOT_EXIST!') + if not os.path.exists(get.tmp_path): return public.return_msg_gettext(False,'TEM_FILE_NOT_EXIST!') plugin_path = '/www/server/panel/plugin/' + get.plugin_name if not os.path.exists(plugin_path): os.makedirs(plugin_path) public.ExecShell("\cp -a -r " + get.tmp_path + '/* ' + plugin_path + '/') @@ -2188,24 +2242,24 @@ class panelPlugin: import shutil shutil.copyfile(icon_sfile,icon_dfile) #----- 增加图标复制 END -----# - public.WriteLog('TYPE_SOFT','INSTALL_THIRD_PARDY_PLUG' ,(json.loads(p_info)['title'],)) - return public.returnMsg(True,'PLUGIN_INSTALL_SUCCESS') + public.write_log_gettext('Software manager','Installed third-party plugin [{}]' ,(json.loads(p_info)['title'],)) + return public.return_msg_gettext(True,'Installation succeeded!') public.ExecShell("rm -rf " + plugin_path) - return public.returnMsg(False,'INSTALL_FAIL') + return public.return_msg_gettext(False,'Installation failed') #导出插件包 def export_zip(self,get): plugin_path = '/www/server/panel/plugin/' + get.plugin_name - if not os.path.exists(plugin_path): return public.returnMsg(False,'INIT_PLUGIN_NOT_EXISTS') + if not os.path.exists(plugin_path): return public.return_msg_gettext(False,'The specified plugin does not exist!') get.sfile = plugin_path + '/' get.dfile = '/www/server/panel/temp/bt_plugin_' + get.plugin_name + '.zip' get.type = 'zip' import files files.files().Zip(get) - if not os.path.exists(get.dfile): return public.returnMsg(False,'PLUG_EXPORT_FAIL') - return public.returnMsg(True,get.dfile) + if not os.path.exists(get.dfile): return public.return_msg_gettext(False,'Export failed, please check permissions!') + return public.return_msg_gettext(True,get.dfile) #获取编译参数 @@ -2243,7 +2297,7 @@ class panelPlugin: get.name = get.name.strip() get.ps = get.ps.strip() if not re.match(r'^\w+$',get.args_name): - return public.returnMsg(False,'COMPLIANT_NAME_ERR') + return public.return_msg_gettext(False,'Non-compliant names can only be numbers, letters, underscores') config_path = os.path.join('install' , get.name , get.args_name) if not os.path.exists(config_path): @@ -2256,18 +2310,18 @@ class panelPlugin: public.writeFile(args_file,get.args) public.writeFile(ps_file,get.ps) - public.WriteLog('TYPE_SOFT','ADD_COMPILATION_PARA',(get.name,get.args_name)) - return public.returnMsg(True,'ADD_SUCCESS') + public.write_log_gettext('Software manager','Add custom compilation parameters: {}:{}',(get.name,get.args_name)) + return public.return_msg_gettext(True,'Setup successfully!') #删除编译参数 def del_make_args(self,get): get.args_name = get.args_name.strip() get.name = get.name.strip() if not re.match(r'^\w+$',get.args_name): - return public.returnMsg(False,'COMPLIANT_NAME_ERR') + return public.return_msg_gettext(False,'Non-compliant names can only be numbers, letters, underscores') config_path = os.path.join('install' , get.name , get.args_name) if not os.path.exists(config_path): - return public.returnMsg(False,'PARA_NOT_EXIST') + return public.return_msg_gettext(False,'The specified custom compilation parameters do not exist!') public.ExecShell("rm -rf {}".format(config_path)) config_file = 'install/' + get.name + '/config.pl' if os.path.exists(config_file): @@ -2275,8 +2329,8 @@ class panelPlugin: if get.args_name in config_data: config_data.remove(get.args_name) public.writeFile(config_file,"\n".join(config_data)) - public.WriteLog('TYPE_SOFT','REMOVE_PARA',(get.name,get.args_name)) - return public.returnMsg(True,'DEL_SUCCESS') + public.write_log_gettext('Software manager','Remove custom compilation parameters: {}:{}',(get.name,get.args_name)) + return public.return_msg_gettext(True,'Successfully deleted') #设置当前编译参数 @@ -2291,5 +2345,5 @@ class panelPlugin: if args_name in config_data: continue config_data.append(args_name) public.writeFile(config_file,"\n".join(config_data)) - public.WriteLog('TYPE_SOFT','SET_SOFTWARE_COMPILATION'.format(get.name,config_data)) - return public.returnMsg(True,'SET_SUCCESS') \ No newline at end of file + public.write_log_gettext('Software manager','Setup software: Custom compilation parameters for {} are configured as: {}'.format(get.name,config_data)) + return public.return_msg_gettext(True,'Setup successfully!') \ No newline at end of file diff --git a/class/panelProjectController.py b/class/panelProjectController.py index 818420ce..322e0036 100644 --- a/class/panelProjectController.py +++ b/class/panelProjectController.py @@ -29,11 +29,11 @@ class ProjectController: } ''' try: # 表单验证 - if args['mod_name'] in ['base']: return public.return_status_code(1000,'错误的调用!') + if args['mod_name'] in ['base']: return public.return_status_code(1000,'wrong call!') public.exists_args('def_name,mod_name',args) - if args['def_name'].find('__') != -1: return public.return_status_code(1000,'调用的方法名称中不能包含“__”字符') - if not re.match(r"^\w+$",args['mod_name']): return public.return_status_code(1000,'调用的模块名称中不能包含\w以外的字符') - if not re.match(r"^\w+$",args['def_name']): return public.return_status_code(1000,'调用的方法名称中不能包含\w以外的字符') + if args['def_name'].find('__') != -1: return public.return_status_code(1000,'Called method name cannot contain [ __ ] characters') + if not re.match(r"^\w+$",args['mod_name']): return public.return_status_code(1000,'The called module name cannot contain characters other than \w') + if not re.match(r"^\w+$",args['def_name']): return public.return_status_code(1000,'The called module name cannot contain characters other than \w') except: return public.get_error_object() # 参数处理 @@ -59,7 +59,7 @@ class ProjectController: else: pdata = args.data else: - pdata = public.dict_obj() + pdata = args # 前置HOOK hook_index = '{}_{}_LAST'.format(mod_name.upper(),def_name.upper()) diff --git a/class/panelRedirect.py b/class/panelRedirect.py index 9d38fe97..5b2a92f4 100644 --- a/class/panelRedirect.py +++ b/class/panelRedirect.py @@ -83,7 +83,7 @@ class panelRedirect: else: sk.connect((d, 443)) except: - return public.returnMsg(False, "CANT_GET_URL") + return public.return_msg_gettext(False, 'Can NOT get target URL') # 计算proxyname md5 def __calc_md5(self,redirectname): import hashlib @@ -112,7 +112,7 @@ class panelRedirect: if get.sitename in sitenamelist: rep = "include.*\/redirect\/.*\*.conf;" if not re.search(rep,ng_conf): - ng_conf = ng_conf.replace("#SSL-END","#SSL-END\n\t%s\n\t" % public.GetMsg("NGINX_REDIRECT_REP") + "include " + ng_redirectfile + ";") + ng_conf = ng_conf.replace("#SSL-END","#SSL-END\n\t%s\n\t" % public.get_msg_gettext('#referenced redirect rule, if commented, the configured redirect rule will be invalid') + "include " + ng_redirectfile + ";") public.writeFile(ng_file,ng_conf) else: @@ -130,18 +130,18 @@ class panelRedirect: if os.path.exists(ap_file): ap_conf = public.readFile(ap_file) if p_conf == "[]": - rep = "\n*%s\n+\s+IncludeOptiona[\s\w\/\.\*]+" % public.GetMsg("NGINX_REDIRECT_REP") + rep = "\n*%s\n+\s+IncludeOptiona[\s\w\/\.\*]+" % public.get_msg_gettext('#referenced redirect rule, if commented, the configured redirect rule will be invalid') ap_conf = re.sub(rep, '', ap_conf) public.writeFile(ap_file, ap_conf) return if sitename in p_conf: - rep = "%s(\n|.)+IncludeOptional.*\/redirect\/.*conf" % public.GetMsg("NGINX_REDIRECT_REP1") + rep = "%s(\n|.)+IncludeOptional.*\/redirect\/.*conf" % public.get_msg_gettext('#referenced redirect rule') rep1 = "combined" if not re.search(rep,ap_conf): - ap_conf = ap_conf.replace(rep1, rep1 + "\n\t%s" % public.GetMsg("NGINX_REDIRECT_REP") +"\n\tIncludeOptional " + ap_redirectfile) + ap_conf = ap_conf.replace(rep1, rep1 + "\n\t%s" % public.get_msg_gettext('#referenced redirect rule, if commented, the configured redirect rule will be invalid') +"\n\tIncludeOptional " + ap_redirectfile) public.writeFile(ap_file,ap_conf) else: - rep = "\n*%s\n+\s+IncludeOptiona[\s\w\/\.\*]+" % public.GetMsg("NGINX_REDIRECT_REP") + rep = "\n*%s\n+\s+IncludeOptiona[\s\w\/\.\*]+" % public.get_msg_gettext('#referenced redirect rule, if commented, the configured redirect rule will be invalid') ap_conf = re.sub(rep,'', ap_conf) public.writeFile(ap_file, ap_conf) @@ -149,52 +149,52 @@ class panelRedirect: def __CheckRedirectStart(self,get,action=""): isError = public.checkWebConfig() if (isError != True): - return public.returnMsg(False, 'GET_ERR_IN_CONFILE') + return public.return_msg_gettext(False, 'An error was detected in the configuration file. Please solve it before proceeding') if action == "create": #检测名称是否重复 if sys.version_info.major < 3: if len(get.redirectname) < 3 or len(get.redirectname) > 15: - return public.returnMsg(False, 'NAME_LEN') + return public.return_msg_gettext(False, 'Database name cannot be more than 16 characters!') else: if len(get.redirectname.encode("utf-8")) < 3 or len(get.redirectname.encode("utf-8")) > 15: - return public.returnMsg(False, 'NAME_LEN') + return public.return_msg_gettext(False, 'Database name cannot be more than 16 characters!') if self.__CheckRedirect(get.sitename,get.redirectname): - return public.returnMsg(False, 'REDIRECT_EXIST') + return public.return_msg_gettext(False, 'Specified redirect name already exists') #检测是否选择域名 if get.domainorpath == "domain": if not json.loads(get.redirectdomain): - return public.returnMsg(False, 'SELECT_RED_DOMAIN') + return public.return_msg_gettext(False, 'Please select redirected domain') else: if not get.redirectpath: - return public.returnMsg(False, 'INPUT_RED_DOMAIN') + return public.return_msg_gettext(False, 'Please enter redirected path') #repte = "[\?\=\[\]\)\(\*\&\^\%\$\#\@\!\~\`{\}\>\<\,\',\"]+" # 检测路径格式 if "/" not in get.redirectpath: - return public.returnMsg(False, "PATH_ERR") + return public.return_msg_gettext(False, 'Path format is incorrect, the format is /xxx') #if re.search(repte, get.redirectpath): - # return public.returnMsg(False, "代理目录不能有以下特殊符号 ?,=,[,],),(,*,&,^,%,$,#,@,!,~,`,{,},>,<,\,',\"]") + # return public.return_msg_gettext(False, "代理目录不能有以下特殊符号 ?,=,[,],),(,*,&,^,%,$,#,@,!,~,`,{,},>,<,\,',\"]") #检测域名是否已经存在配置文件 repeatdomain = self.__CheckRepeatDomain(get,action) if repeatdomain: - return public.returnMsg(False, 'RED_DOMAIN_EXIST' , (repeatdomain,)) + return public.return_msg_gettext(False, 'Redirected domain already exists {}' , (repeatdomain,)) #检测路径是否有存在配置文件 repeatpath = self.__CheckRepeatPath(get) if repeatpath: - return public.returnMsg(False, 'RED_DOMAIN_EXIST' , (repeatpath,)) + return public.return_msg_gettext(False, 'Redirected domain already exists {}' , (repeatpath,)) #检测目标URL格式 rep = "http(s)?\:\/\/([a-zA-Z0-9][-a-zA-Z0-9]{0,62}\.)+([a-zA-Z0-9][a-zA-Z0-9]{0,62})+.?" if not re.match(rep, get.tourl): - return public.returnMsg(False, 'URL_FORMAT_ERR' ,(get.tourl,)) + return public.return_msg_gettext(False, 'The target URL format is incorrect {}' ,(get.tourl,)) #检测目标URL是否可用 #if self.__CheckRedirectUrl(get): - # return public.returnMsg(False, '目标URL无法访问') + # return public.return_msg_gettext(False, '目标URL无法访问') #检查目标URL的域名和被重定向的域名是否一样 if get.domainorpath == "domain": for d in json.loads(get.redirectdomain): tu = self.GetToDomain(get.tourl) if d == tu: - return public.returnMsg(False,public.GetMsg("DOMAIN_SAMEAS_URL",(d,))) + return public.return_msg_gettext(False,public.get_msg_gettext('Domain name {} is the same as the target domain name, please deselect it',(d,))) if get.domainorpath == "path": domains = self.GetAllDomain(get.sitename) @@ -203,7 +203,7 @@ class panelRedirect: for d in domains: ad = "%s%s" % (d,get.redirectpath) #站点域名+重定向路径 if tu == ad: - return public.GetMsg("URL_SAMEAS_REDPATH",(tu,)) + return public.get_msg_gettext('{}, the target URL is the same as the redirected path',(tu,)) #创建重定向 def CreateRedirect(self,get): @@ -226,7 +226,7 @@ class panelRedirect: self.SetRedirectApache(get.sitename) self.SetRedirect(get) public.serviceReload() - return public.returnMsg(True, 'CREATE_SUCCESS') + return public.return_msg_gettext(True, 'Successfully created file!') # 设置重定向 def SetRedirect(self,get): @@ -331,7 +331,7 @@ class panelRedirect: for i in range(len(p_conf) - 1, -1, -1): if get.sitename == p_conf[i]["sitename"] and p_conf[i]["redirectname"]: del(p_conf[i]) - return public.returnMsg(False, '%s
                                      ' % public.GetMsg("HAVE_ERR") + isError.replace("\n",'
                                      ') + '
                                      ') + return public.return_msg_gettext(False, '%s
                                      ' % public.get_msg_gettext('Sorry, something went wrong') + isError.replace("\n",'
                                      ') + '
                                      ') else: redirectname_md5 = self.__calc_md5(get.redirectname) @@ -361,7 +361,7 @@ class panelRedirect: self.SetRedirectApache(get.sitename) if not hasattr(get,'notreload'): public.serviceReload() - return public.returnMsg(True, 'EDIT_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') def del_redirect_multiple(self,get): ''' @@ -384,9 +384,9 @@ class panelRedirect: continue del_successfully.append(redirectname) except: - del_failed[redirectname]=public.getMsg('DEL_ERROR1') + del_failed[redirectname]=public.get_msg_gettext('There was an error deleting, please try again.') public.serviceReload() - return {'status': True, 'msg': public.getMsg('DEL_REDIRECT_MULTIPLE',(','.join(del_successfully),)), 'error': del_failed, + return {'status': True, 'msg': public.get_msg_gettext('Delete redirects [{}] successfully',(','.join(del_successfully),)), 'error': del_failed, 'success': del_successfully} def DeleteRedirect(self,get,multiple=None): @@ -404,7 +404,7 @@ class panelRedirect: self.SetRedirectApache(get.sitename) if not multiple: public.serviceReload() - return public.returnMsg(True, 'DEL_SUCCESS') + return public.return_msg_gettext(True, 'Successfully deleted') def GetRedirectList(self,get): redirectconf = self.__read_config(self.__redirectfile) @@ -433,7 +433,7 @@ class panelRedirect: # get.redirectpath = "/" # get.redirectdomain = "[]" # get.sitename = sitename - # get.redirectname = public.GetMsg("OLD_CONF") + # get.redirectname = public.get_msg_gettext('Old configuration') # get.type = 1 # get.holdpath = 1 @@ -470,7 +470,7 @@ class panelRedirect: conf = re.sub(rep, "", old_conf) public.writeFile(conf_path, conf) public.serviceReload() - return public.returnMsg(False, 'CLEAR_OLD_RED') + return public.return_msg_gettext(False, 'Old redirection cleaned') # 取重定向配置文件 def GetRedirectFile(self,get): @@ -484,7 +484,7 @@ class panelRedirect: get.path = "%s/panel/vhost/%s/redirect/%s/%s_%s.conf" % (self.setupPath, get.webserver, sitename,proxyname_md5,sitename) for i in conf: if redirectname == i["redirectname"] and sitename == i["sitename"] and i["type"] != 1: - return public.returnMsg(False, 'RED_ALREADY_STOP') + return public.return_msg_gettext(False, 'Redirection suspended') f = files.files() return f.GetFileBody(get),get.path @@ -493,7 +493,7 @@ class panelRedirect: import files f = files.files() return f.SaveFileBody(get) - # return public.returnMsg(True, '保存成功') + # return public.return_msg_gettext(True, '保存成功') def __CheckRedirect(self,sitename,redirectname): conf_data = self.__read_config(self.__redirectfile) diff --git a/class/panelRun.py b/class/panelRun.py index 77935968..692b957f 100644 --- a/class/panelRun.py +++ b/class/panelRun.py @@ -20,7 +20,7 @@ class panelRun: __run_config_path = '{}/config/run_config'.format(__panel_path) __run_pids_path = '{}/logs/run_pids'.format(__panel_path) __run_logs_path = '{}/logs/run_logs'.format(__panel_path) - __log_name = '开机启动项' + __log_name = 'Startup items' def __init__(self): @@ -69,7 +69,7 @@ class panelRun: if get: run_name = get['run_name'] run_file = '{}/{}'.format(self.__run_config_path,run_name) if not os.path.isfile(run_file): - return public.returnMsg(False,'启动配置不存在!') + return public.return_msg_gettext(False,'Configuration file not exist') run_info = json.loads(public.readFile(run_file)) return run_info @@ -98,14 +98,14 @@ class panelRun: run_script_args = get['run_script_args'] run_env = json.loads(get['run_env']) if not os.path.exists(run_path): - return public.returnMsg(False,'指定运行目录{}不存在!'.format(run_path)) + return public.return_msg_gettext(False,'The specified run directory {} does not exist!'.format(run_path)) if not re.match(r'^\w+$',run_name): - return public.returnMsg(False, '启动项名称格式不正确,支持:[a-zA-Z0-9_]!') + return public.return_msg_gettext(False, 'The startup item name format is incorrect, support: [a-zA-Z0-9_]!') run_file = '{}/{}'.format(self.__run_config_path,run_name) if os.path.exists(run_file): - return public.returnMsg(False,'启动配置已存在!') + return public.return_msg_gettext(False,'Launch configuration already exists!') run_info = { 'run_title': run_title, @@ -117,8 +117,8 @@ class panelRun: } run_info = json.dumps(run_info) public.writeFile(run_file,run_info) - public.WriteLog(self.__log_name,'创建启动项[]成功!'.format(run_title)) - return public.returnMsg(True,'创建成功!') + public.write_log_gettext(self.__log_name,'Create startup item [] successful!'.format(run_title)) + return public.return_msg_gettext(True,'Successfully created') def modify_run(self,get): @@ -145,15 +145,15 @@ class panelRun: run_env = json.loads(get['run_env']) if not os.path.exists(run_path): - return public.returnMsg(False,'指定运行目录{}不存在!'.format(run_path)) + return public.return_msg_gettext(False,'The specified run directory {} does not exist!',(run_path,)) if not re.match(r'^\w+$',run_name): - return public.returnMsg(False, '启动项名称格式不正确,支持:[a-zA-Z0-9_]!') + return public.return_msg_gettext(False, 'The startup item name format is incorrect, support: [a-zA-Z0-9_]!') run_file = '{}/{}'.format(self.__run_config_path,run_name) if not os.path.exists(run_file): - return public.returnMsg(False,'启动配置不存在!') + return public.return_msg_gettext(False,'The launch configuration does not exist!') run_info = json.loads(public.readFile(run_file)) run_info['run_title'] = run_title @@ -162,8 +162,8 @@ class panelRun: run_info['run_env'] = run_env run_info = json.dumps(run_info) public.writeFile(run_file,run_info) - public.WriteLog(self.__log_name,'修改启动项[]成功!'.format(run_title)) - return public.returnMsg(True,'修改成功!') + public.write_log_gettext(self.__log_name,'Modify startup item [{}] successful!',(run_title,)) + return public.return_msg_gettext(True,'Successfully modified') def remove_run(self,get): @@ -178,11 +178,11 @@ class panelRun: run_name = get['run_name'] run_file = '{}/{}'.format(self.__run_config_path,run_name) if not os.path.isfile(run_file): - return public.returnMsg(False,'启动配置不存在!') + return public.return_msg_gettext(False,'The launch configuration does not exist!') os.remove(run_file) - public.WriteLog(self.__log_name,'删除启动项[]成功!'.format(run_name)) - return public.returnMsg(True,'删除成功!') + public.write_log_gettext(self.__log_name,'Delete startup item [{}] successful!',(run_name,)) + return public.return_msg_gettext(True,'successfully deleted') def set_run_status(self,get): ''' @@ -199,14 +199,14 @@ class panelRun: run_file = '{}/{}'.format(self.__run_config_path,run_name) if not os.path.isfile(run_file): - return public.returnMsg(False,'启动配置不存在!') + return public.return_msg_gettext(False,'launch configuration does not exist!') run_info = json.loads(public.readFile(run_file)) run_info['run_status'] = run_status run_info = json.dumps(run_info) public.writeFile(run_file,run_info) - public.WriteLog(self.__log_name,'设置启动项[]状态成功!'.format(run_info['title'])) - return public.returnMsg(True,'设置成功!') + public.write_log_gettext(self.__log_name,'Setting startup item [{}] status succeeded!',(run_info['title'],)) + return public.return_msg_gettext(True,'Setup successfully!') def stop_run(self,run_name = None): @@ -219,7 +219,7 @@ class panelRun: pid = self.get_run_pid(run_name) if not pid: return True os.kill(pid,signal.SIGKILL) - public.WriteLog(self.__log_name,'关闭启动项[]成功!'.format(run_name)) + public.write_log_gettext(self.__log_name,'Close startup item [{}] successful!',(run_name,)) return True @@ -267,9 +267,9 @@ class panelRun: @return dict ''' pid = self.get_run_pid(run_name) - if not pid: return public.returnMsg(False,'未启动') + if not pid: return public.return_msg_gettext(False,'Not run') process_info = self.get_process_info(pid) - if not process_info: return public.returnMsg(False,'无法获取进程信息') + if not process_info: return public.return_msg_gettext(False,'Unable to get process information') return process_info def get_process_info(self,pid): @@ -281,7 +281,7 @@ class panelRun: ''' process_info = {} p = psutil.Process(pid) - status_ps = {'sleeping':'睡眠','running':'活动'} + status_ps = {'sleeping':'sleeping','running':'running'} with p.oneshot(): p_mem = p.memory_full_info() if p_mem.uss + p_mem.rss + p_mem.pss + p_mem.data == 0: return False @@ -368,7 +368,7 @@ class panelRun: time.sleep(1) pid = self.get_script_pid(run_info) public.writeFile(pid_file,str(pid)) - public.WriteLog(self.__log_name, '开机启动{}成功, PID: {}'.format(run_name,pid)) + public.write_log_gettext(self.__log_name, 'Startup {} successful, PID:{}',(run_name,pid,)) return True diff --git a/class/panelSSL.py b/class/panelSSL.py index 7a01bc96..7d43c329 100644 --- a/class/panelSSL.py +++ b/class/panelSSL.py @@ -20,7 +20,7 @@ try: except: pass class panelSSL: - __APIURL = 'https://brandnew.aapanel.com/api/user' + __APIURL = 'https://www.aapanel.com/api/user' __APIURL2 = 'http://www.bt.cn/api/Cert' __UPATH = 'data/userInfo.json' __PUBKEY = 'data/public.key' @@ -65,13 +65,13 @@ class panelSSL: userinfo['token'] = result['res']['access_token'] public.writeFile(self.__UPATH, json.dumps(userinfo)) session['focre_cloud'] = True - return public.returnMsg(True,'Bind successfully') + return public.return_msg_gettext(True,'Bind successfully') else: - return public.returnMsg(False,'Invalid username or email or password! please check and try again!') + return public.return_msg_gettext(False,'Invalid username or email or password! please check and try again!') except Exception as ex: bind = 'data/bind.pl' if os.path.exists(bind): os.remove(bind) - return public.returnMsg(False, '%s
                                      %s' % (public.GetMsg("CONNECT_ERR"), str(rtmp))) + return public.return_msg_gettext(False, '%s
                                      %s' % (public.get_msg_gettext('Failed to connect server!'), str(rtmp))) #删除Token def DelToken(self,get): @@ -83,7 +83,7 @@ class panelSSL: public.ExecShell("rm -f " + self.__UPATH) session['focre_cloud'] = True - return public.returnMsg(True,"SSL_BTUSER_UN") + return public.return_msg_gettext(True,'Unbound!') #获取用户信息 def GetUserInfo(self,get): @@ -93,19 +93,19 @@ class panelSSL: userTmp = {} userTmp['username'] = self.__userInfo['email'][0:3]+'****'+self.__userInfo['email'][-4:] result['status'] = True - result['msg'] = public.getMsg('SSL_GET_SUCCESS') + result['msg'] = public.get_msg_gettext('Got successfully!') result['data'] = userTmp else: userTmp = {} - userTmp['username'] = public.getMsg('SSL_NOT_BTUSER') + userTmp['username'] = public.get_msg_gettext('Please bind your account!') result['status'] = False - result['msg'] = public.getMsg('SSL_NOT_BTUSER') + result['msg'] = public.get_msg_gettext('Please bind your account!') result['data'] = userTmp except: userTmp = {} - userTmp['username'] = public.getMsg('SSL_NOT_BTUSER') + userTmp['username'] = public.get_msg_gettext('Please bind your account!') result['status'] = False - result['msg'] = public.getMsg('SSL_NOT_BTUSER') + result['msg'] = public.get_msg_gettext('Please bind your account!') result['data'] = userTmp return result @@ -153,7 +153,7 @@ class panelSSL: import panelSite panelSite.panelSite().SetSSLConf(get) public.serviceReload() - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') #生成商业证书支付订单 def apply_order_pay(self,args): @@ -311,7 +311,7 @@ class panelSSL: verify_info['paths'] = [] verify_info['hosts'] = [] if verify_info['data']['application']['status'] == 'ongoing': - return public.returnMsg(False,'订单出现问题,CA正在人工验证,若24小时内依然出现此提示,请联系宝塔') + return public.return_msg_gettext(False,'订单出现问题,CA正在人工验证,若24小时内依然出现此提示,请联系宝塔') for dinfo in verify_info['data']['dcvList']: is_https = dinfo['dcvMethod'] == 'HTTPS_CSR_HASH' if is_https: @@ -356,7 +356,7 @@ class panelSSL: #发送请求 def request(self,dname): self.__PDATA['data'] = json.dumps(self.__PDATA['data']) - result= public.returnMsg(False,'The request failed, please try again later!') + result= public.return_msg_gettext(False,'The request failed, please try again later!') try: result = public.httpPost(self.__APIURL2 + '/' + dname,self.__PDATA) result = json.loads(result) @@ -378,7 +378,7 @@ class panelSSL: rs = public.httpPost(self.__APIURL + '/GetSSLList',self.__PDATA) try: result = json.loads(rs) - except: return public.returnMsg(False,'SSL_ORDER_GET_FAILED') + except: return public.return_msg_gettext(False,'Failed to get, please try again later!') result['data'] = self.En_Code(result['data']) for i in range(len(result['data'])): @@ -408,11 +408,11 @@ class panelSSL: #当申请二级域名为www时,检测主域名是否绑定到同一网站 if get.domain[:4] == 'www.': if not public.M('domain').where('name=? AND pid=?',(get.domain[4:],get.id)).count(): - return public.returnMsg(False,"Apply for [%s] certificate to verify [%s] Please bind [%s] and resolve to the site!" % (get.domain,get.domain[4:],get.domain[4:])) + return public.return_msg_gettext(False,"Apply for [{}] certificate to verify [{}] Please bind [{}] and resolve to the site!",(get.domain,get.domain[4:],get.domain[4:])) #检测是否开启强制HTTPS if not self.CheckForceHTTPS(get.siteName): - return public.returnMsg(False,'SSL_ORDER_HTTPS_ERR') + return public.return_msg_gettext(False,'[Force HTTPS] is enabled on the current website, please turn off this function before applying for an SSL certificate!') #获取真实网站运行目录 runPath = self.GetRunPath(get) @@ -423,19 +423,19 @@ class panelSSL: authfile = get.path + '/.well-known/pki-validation/fileauth.txt' if not self.CheckDomain(get): if not os.path.exists(authfile): - return public.returnMsg(False,'CANT_CREATE',(authfile,)) + return public.return_msg_gettext(False,'Cannot create [{}]',(authfile,)) else: msg = '''{err_msg}
                                      {c_url}

                                      {err_msg1}

                                      {err_msg2}
                                      {err_msg3}
                                      {err_msg4}'''.format(c_url = self._check_url, - err_msg=public.getMsg('SSL_ERR_MSG'), - err_msg1=public.getMsg('SSL_ERR_MSG1'), - err_msg2=public.getMsg('SSL_ERR_MSG2'), - err_msg3=public.getMsg('SSL_ERR_MSG3'), - err_msg4=public.getMsg('SSL_ERR_MSG4')) - return public.returnMsg(False,msg) + err_msg=public.get_msg_gettext('Cannot access verification file correctly'), + err_msg1=public.get_msg_gettext('Possible reason:'), + err_msg2=public.get_msg_gettext('1. The resolution is not correct, or the resolution is not effective [Please resolve the domain name correctly, or wait for the resolution to take effect and try again]'), + err_msg3=public.get_msg_gettext('2. Check if there is 301/302 redirection set up [please temporarily turn off the redirection related configuration]'), + err_msg4=public.get_msg_gettext('3. Check whether the website is set to force HTTPS [please turn off the force HTTPS function temporarily]')) + return public.return_msg_gettext(False,msg) action = 'GetDVSSL' if hasattr(get,'partnerOrderId'): @@ -523,7 +523,7 @@ class panelSSL: try: sslInfo = json.loads(tmp) except: - return public.returnMsg(False,tmp) + return public.return_msg_gettext(False,tmp) sslInfo['data'] = self.En_Code(sslInfo['data']) try: @@ -531,13 +531,13 @@ class panelSSL: if not os.path.exists(spath): public.ExecShell("mkdir -p '" + spath + "'") public.writeFile(spath + '/fileauth.txt',sslInfo['data']['authValue']) except: - return public.returnMsg(False,'SSL_CHECK_WRITE_ERR') + return public.return_msg_gettext(False,'Verification error!') try: result = json.loads(public.httpPost(self.__APIURL + '/Completed',self.__PDATA)) if 'data' in result: result['data'] = self.En_Code(result['data']) except: - result = public.returnMsg(True,'CHECKING') + result = public.return_msg_gettext(True,'Checking...') n = 0; my_ok = False while True: @@ -598,9 +598,9 @@ class panelSSL: import panelSite panelSite.panelSite().SetSSLConf(get) public.serviceReload() - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') except: - return public.returnMsg(False,'SET_ERROR') + return public.return_msg_gettext(False,'Failed to set') result['data'] = self.En_Code(result['data']) return result @@ -632,9 +632,9 @@ class panelSSL: import panelSite panelSite.panelSite().SetSSLConf(get) public.serviceReload() - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') except Exception as ex: - return public.returnMsg(False,'SET_ERROR,' + public.get_error_info()) + return public.return_msg_gettext(False,'Failed to set \n{}',(public.get_error_info(),)) #获取证书列表 def GetCertList(self,get): @@ -657,17 +657,17 @@ class panelSSL: def RemoveCert(self,get): try: vpath = '/www/server/panel/vhost/ssl/' + get.certName.replace("*.",'') - if not os.path.exists(vpath): return public.returnMsg(False,'CRET_NOT_EXIST') + if not os.path.exists(vpath): return public.return_msg_gettext(False,'Certificate does NOT exist!') public.ExecShell("rm -rf " + vpath) - return public.returnMsg(True,'CRET_DEL') + return public.return_msg_gettext(True,'Certificate deleted!') except: - return public.returnMsg(False,'CRET_DEL_FAIL') + return public.return_msg_gettext(False,'Failed to delete!') #保存证书 def SaveCert(self,get): try: certInfo = self.GetCertName(get) - if not certInfo: return public.returnMsg(False,'CRET_RESOLVE_FAIL') + if not certInfo: return public.return_msg_gettext(False,'Certificate parsing failed') vpath = '/www/server/panel/vhost/ssl/' + certInfo['subject'] vpath=vpath.replace("*.",'') if not os.path.exists(vpath): @@ -675,14 +675,14 @@ class panelSSL: public.writeFile(vpath + '/privkey.pem',public.readFile(get.keyPath)) public.writeFile(vpath + '/fullchain.pem',public.readFile(get.certPath)) public.writeFile(vpath + '/info.json',json.dumps(certInfo)) - return public.returnMsg(True,'CRET_SAVE_SUSSESS') + return public.return_msg_gettext(True,'Successfully saved certificate!') except: - return public.returnMsg(False,'CRET_SAVE_FAIL') + return public.return_msg_gettext(False,'Failed to save certificate!') #读取证书 def GetCert(self,get): vpath = os.path.join('/www/server/panel/vhost/ssl' , get.certName.replace("*.",'')) - if not os.path.exists(vpath): return public.returnMsg(False,'CRET_NOT_EXIST') + if not os.path.exists(vpath): return public.return_msg_gettext(False,'Certificate does NOT exist!') data = {} data['privkey'] = public.readFile(vpath + '/privkey.pem') data['fullchain'] = public.readFile(vpath + '/fullchain.pem') @@ -822,13 +822,13 @@ class panelSSL: # 手动一键续签 def renew_lets_ssl(self, get): if not os.path.exists('vhost/cert/crontab.json'): - return public.returnMsg(False,'SSL_RENEW_ERR') + return public.return_msg_gettext(False,'There are currently no certificates to renew!') old_list = json.loads(public.ReadFile("vhost/cert/crontab.json")) cron_list = old_list if hasattr(get, 'siteName'): if not get.siteName in old_list: - return public.returnMsg(False,'WEBSITE_SSL_RENEW_ERR') + return public.return_msg_gettext(False,'There is no certificate that can be renewed on the current website..') cron_list = {} cron_list[get.siteName] = old_list[get.siteName] diff --git a/class/panelSafe.py b/class/panelSafe.py index 49303637..745ac227 100644 --- a/class/panelSafe.py +++ b/class/panelSafe.py @@ -100,7 +100,7 @@ class safe: def checkPHPINI(self): setupPath = '/www/server'; - phps = ['52','53','54','55','56','70','71'] + phps = public.get_php_versions() rep = "disable_functions\s*=\s*(.+)\n" defs = ['passthru','exec','system','chroot','chgrp','chown','shell_exec','popen','ini_alter','ini_restore','dl','openlog','syslog','readlink','symlink','popepassthru'] data = [] diff --git a/class/panelSearch.py b/class/panelSearch.py index ca9ded2c..11283ff2 100644 --- a/class/panelSearch.py +++ b/class/panelSearch.py @@ -189,10 +189,10 @@ class panelSearch: noword 1 不输出行信息 0 默认 ''' def get_search(self, args): - if 'text' not in args or not args.text: return {'error': 'Search content cannot be empty'} - if 'exts' not in args or not args.exts: return {'error': 'The suffix cannot be empty; please enter [ *.* ] to search all files'} - if 'path' not in args or not args.path or args.path == '/': return {'error': 'The directory cannot be empty or /'} - if not os.path.isdir(args.path): return {'error': 'Directory does not exist'} + if 'text' not in args or not args.text: return {'error': public.get_msg_gettext('Search content cannot be empty')} + if 'exts' not in args or not args.exts: return {'error': public.get_msg_gettext('The suffix cannot be empty; please enter [ *.* ] to search all files')} + if 'path' not in args or not args.path or args.path == '/': return {'error': public.get_msg_gettext('The directory cannot be empty or /')} + if not os.path.isdir(args.path): return {'error': public.get_msg_gettext('Directory does not exist')} text=args.text exts=args.exts path=args.path @@ -217,11 +217,11 @@ class panelSearch: noword 1 不输出行信息 0 默认 ''' def get_replace(self, args): - if 'text' not in args or not args.text: return {'error': 'Search content cannot be empty'} - if 'rtext' not in args or not args.text: return {'error': 'The content to be replaced cannot be empty'} - if 'exts' not in args or not args.exts: return {'error': 'The suffix cannot be empty; please enter [ *.* ] to search all files'} - if 'path' not in args or not args.path or args.path == '/': return {'error': 'The directory cannot be empty or /'} - if not os.path.isdir(args.path): return {'error': 'Directory does not exist'} + if 'text' not in args or not args.text: return {'error': public.get_msg_gettext('Search content cannot be empty')} + if 'rtext' not in args or not args.text: return {'error': public.get_msg_gettext('The content to be replaced cannot be empty')} + if 'exts' not in args or not args.exts: return {'error': public.get_msg_gettext('The suffix cannot be empty; please enter [ *.* ] to search all files')} + if 'path' not in args or not args.path or args.path == '/': return {'error': public.get_msg_gettext('The directory cannot be empty or /')} + if not os.path.isdir(args.path): return {'error': public.get_msg_gettext('Directory does not exist')} is_backup = int(args.isbackup) if 'isbackup' in args else 0 text = args.text rtext = args.rtext diff --git a/class/panelSite.py b/class/panelSite.py index 0956a5b3..3e867ceb 100644 --- a/class/panelSite.py +++ b/class/panelSite.py @@ -18,6 +18,7 @@ except: pass from panelRedirect import panelRedirect import site_dir_auth +import one_key_wp class panelSite(panelRedirect): @@ -230,14 +231,14 @@ class panelSite(panelRedirect): listen_port=self.sitePort, listen_ipv6=listen_ipv6, site_path=self.sitePath, - ssl_start_msg=public.getMsg('NGINX_CONF_MSG1'), - err_page_msg=public.getMsg('NGINX_CONF_MSG2'), - php_info_start=public.getMsg('NGINX_CONF_MSG3'), + ssl_start_msg=public.get_msg_gettext('SSL related configuration, do NOT delete or modify the next line of commented-out 404 rules'), + err_page_msg=public.get_msg_gettext('Error page configuration, allowed to be commented, deleted or modified'), + php_info_start=public.get_msg_gettext('PHP reference configuration, allowed to be commented, deleted or modified'), php_version=self.phpVersion, setup_path=self.setupPath, - rewrite_start_msg=public.getMsg('NGINX_CONF_MSG4'), - description=public.GetMsg("NGINX_CONF_BAN_FILE_DIR"), - description1=public.GetMsg("NGINX_CONF_ONECLICK_SET_SSL_DIR"), + rewrite_start_msg=public.get_msg_gettext('URL rewrite rule reference, any modification will invalidate the rewrite rules set by the panel'), + description=public.get_msg_gettext('# Forbidden files or directories'), + description1=public.get_msg_gettext('# Directory verification related settings for one-click application for SSL certificate'), log_path=public.GetConfigValue('logs_path'), site_name=self.siteName ) @@ -267,7 +268,7 @@ class panelSite(panelRedirect): # 写主配置httpd_config.conf # 操作默认监听配置 if not self.sitePath: - return public.returnMsg(False, "Not specify parameter [sitePath]") + return public.return_msg_gettext(False, "Not specify parameter [sitePath]") if init_args: self.siteName = init_args['sitename'] self.phpVersion = init_args['phpv'] @@ -451,8 +452,8 @@ include /www/server/panel/vhost/openlitespeed/proxy/BTSITENAME/*.conf continue create_successfully[domains[0]] = create_other except: - create_failed[domains[0]] = public.getMsg('CREATE_WEBSITE_ERR') - return {'status': True, 'msg': public.getMsg('CREATE_WEBSITE_MULTIPLE', (','.join(create_successfully),)), + create_failed[domains[0]] = public.get_msg_gettext('There was an error creating, please try again.') + return {'status': True, 'msg': public.get_msg_gettext('Create the website [ {} ] successfully', (','.join(create_successfully),)), 'error': create_failed, 'success': create_successfully} @@ -477,35 +478,66 @@ include /www/server/panel/vhost/openlitespeed/proxy/BTSITENAME/*.conf public.serviceReload() return res + # 检测enable-php-00.conf + def check_php_conf(self): + try: + file = '/www/server/nginx/conf/enable-php.conf' + if public.get_webserver() != "nginx": + return + if os.path.exists(file): + return + php_v = os.listdir('/www/server/php') + if not php_v: + return + conf = public.readFile('/www/server/nginx/conf/enable-php-{}.conf'.format(php_v[0])) + public.writeFile(file,conf) + except: + pass + # 添加站点 def AddSite(self, get, multiple=None): + if not get.path: + return public.return_msg_gettext(False, "Please fill in the website path") + if get.path == "/": + return public.return_msg_gettext(False, "The website path cannot be the root directory [/]") + rep_email = r"[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?" + if hasattr(get, 'email'): + if not re.search(rep_email,get.email): + return public.return_msg_gettext(False, "Please check if the [Email] format correct") + if hasattr(get,'password') and hasattr(get,'pw_weak'): + l = public.check_password(get.password) + if l == 0 and get.pw_weak == 'off': + return public.return_msg_gettext(False,'Password very weak, if you are sure to use it, please tick [ Allow weak passwords ]') + #判断Mysql PHP 没有安装不能继续 + if not os.path.exists("/www/server/mysql") or not os.path.exists("/www/server/php"): + return public.return_msg_gettext(False, + 'Please install Mysql and PHP first!') self.check_default() + self.check_php_conf() isError = public.checkWebConfig() if isError != True: - return public.returnMsg(False, 'ERROR: %s

                                      ' % public.GetMsg( - "GET_ERR_IN_CONFILE") + isError.replace("\n", '
                                      ') + '
                                      ') - - import json, files + return public.return_msg_gettext(False, 'ERROR: %s

                                      ' % public.get_msg_gettext( + 'An error was detected in the configuration file. Please solve it before proceeding') + isError.replace("\n", '
                                      ') + '
                                      ') + import json get.path = self.__get_site_format_path(get.path) - if not public.check_site_path(get.path): a,c = public.get_sys_path() - return public.returnMsg(False,'Please do not set the website root directory to the system main directory:
                                      {}'.format("
                                      ".join(a+c))) + return public.return_msg_gettext(False,'Please do not set the website root directory to the system main directory:
                                      {}'.format("
                                      ".join(a+c))) try: siteMenu = json.loads(get.webname) except: - return public.returnMsg(False, 'PARAMETER_WEBNAME_ERR') + return public.return_msg_gettext(False, 'The format of the webname parameter is incorrect, it should be a parseable JSON string') self.siteName = self.ToPunycode(siteMenu['domain'].strip().split(':')[0]).strip().lower() self.sitePath = self.ToPunycodePath(self.GetPath(get.path.replace(' ', ''))).strip() self.sitePort = get.port.strip().replace(' ', '') if self.sitePort == "": get.port = "80" - if not public.checkPort(self.sitePort): return public.returnMsg(False, 'SITE_ADD_ERR_PORT') + if not public.checkPort(self.sitePort): return public.return_msg_gettext(False, 'Port range is incorrect! should be between 100-65535') for domain in siteMenu['domainlist']: if not len(domain.split(':')) == 2: continue - if not public.checkPort(domain.split(':')[1]): return public.returnMsg(False, 'SITE_ADD_ERR_PORT') + if not public.checkPort(domain.split(':')[1]): return public.return_msg_gettext(False, 'Port range is incorrect! should be between 100-65535') if hasattr(get, 'version'): self.phpVersion = get.version.replace(' ', '') @@ -520,40 +552,40 @@ include /www/server/panel/vhost/openlitespeed/proxy/BTSITENAME/*.conf if self.phpVersion == php_v['version']: is_phpv = True break - if not is_phpv: return public.returnMsg(False, 'PHP_NOT_EXISTS') + if not is_phpv: return public.return_msg_gettext(False, 'Requested PHP version does NOT exist!') domain = None # if siteMenu['count']: # domain = get.domain.replace(' ','') #表单验证 - if not self.__check_site_path(self.sitePath): return public.returnMsg(False,'PATH_ERROR') - if len(self.phpVersion) < 2: return public.returnMsg(False,'SITE_ADD_ERR_PHPEMPTY') - reg = r"^([\w\-\*]{1,100}\.){1,4}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$" - if not re.match(reg, self.siteName): return public.returnMsg(False, 'SITE_ADD_ERR_DOMAIN') - if self.siteName.find('*') != -1: return public.returnMsg(False, 'SITE_ADD_ERR_DOMAIN_TOW') - if self.sitePath[-1] == '.': return public.returnMsg(False, 'DIR_END_WITH', ("'.'",)) + if not self.__check_site_path(self.sitePath): return public.return_msg_gettext(False,'System critical directory cannot be used as site directory') + if len(self.phpVersion) < 2: return public.return_msg_gettext(False,'PHP version cannot be empty') + reg = r"^([\w\-\*]{1,100}\.){1,24}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$" + if not re.match(reg, self.siteName): return public.return_msg_gettext(False, 'Format of primary domain is incorrect') + if self.siteName.find('*') != -1: return public.return_msg_gettext(False, 'Primary domain cannot be wildcard DNS record') + if self.sitePath[-1] == '.': return public.return_msg_gettext(False, 'DIR_END_WITH', ("'.'",)) if not domain: domain = self.siteName # 是否重复 sql = public.M('sites') - if sql.where("name=?", (self.siteName,)).count(): return public.returnMsg(False, 'SITE_ADD_ERR_EXISTS') + if sql.where("name=?", (self.siteName,)).count(): return public.return_msg_gettext(False, 'The site you tried to add already exists!') opid = public.M('domain').where("name=?", (self.siteName,)).getField('pid') if opid: if public.M('sites').where('id=?', (opid,)).count(): - return public.returnMsg(False, 'SITE_ADD_ERR_DOMAIN_EXISTS') + return public.return_msg_gettext(False, 'The domain you tried to add already exists!') public.M('domain').where('pid=?', (opid,)).delete() if public.M('binding').where('domain=?', (self.siteName,)).count(): - return public.returnMsg(False, 'SITE_ADD_ERR_DOMAIN_EXISTS') + return public.return_msg_gettext(False, 'The domain you tried to add already exists!') # 创建根目录 if not os.path.exists(self.sitePath): try: os.makedirs(self.sitePath) except Exception as ex: - return public.returnMsg(False, 'CREATE_SITE_DIR_ERR', (ex,)) + return public.return_msg_gettext(False, 'Failed to create site document root, {}', (ex,)) public.ExecShell('chmod -R 755 ' + self.sitePath) public.ExecShell('chown -R www:www ' + self.sitePath) @@ -594,10 +626,9 @@ set $bt_safe_open "{}/:/tmp/";'''.format(self.sitePath) result = self.openlitespeed_add_site(get) # 检查处理结果 - if not result: return public.returnMsg(False, 'SITE_ADD_ERR_WRITE') + if not result: return public.return_msg_gettext(False, 'Failed to add, write configuraton ERROR!') - ps = get.ps - ps = public.xssencode(ps) + ps = public.xssencode2(get.ps) # 添加放行端口 if self.sitePort != '80': import firewalls @@ -606,9 +637,13 @@ set $bt_safe_open "{}/:/tmp/";'''.format(self.sitePath) firewalls.firewalls().AddAcceptPort(get) if not hasattr(get,'type_id'): get.type_id = 0 + if not hasattr(get,'project_type'): get.project_type = "PHP" public.check_domain_cloud(self.siteName) + # 统计wordpress安装次数 + if get.project_type == 'WP': + public.count_wp() #写入数据库 - get.pid = sql.table('sites').add('name,path,status,ps,type_id,addtime',(self.siteName,self.sitePath,'1',ps,get.type_id,public.getDate())) + get.pid = sql.table('sites').add('name,path,status,ps,type_id,addtime,project_type',(self.siteName,self.sitePath,'1',ps,get.type_id,public.getDate(),get.project_type)) #添加更多域名 for domain in siteMenu['domainlist']: @@ -653,11 +688,12 @@ set $bt_safe_open "{}/:/tmp/";'''.format(self.sitePath) data['databaseStatus'] = True data['databaseUser'] = get.datauser data['databasePass'] = get.datapassword + data['d_id'] = str(public.M('databases').where('pid=?',(get.pid,)).field('id').find()['id']) if not multiple: public.serviceReload() data = self._set_ssl(get, data, siteMenu) data = self._set_redirect(get, data) - public.WriteLog('TYPE_SITE', 'SITE_ADD_SUCCESS', (self.siteName,)) + public.write_log_gettext('Site manager', 'Successfully added site [{}]!', (self.siteName,)) return data def _set_redirect(self, get, data): @@ -745,10 +781,10 @@ set $bt_safe_open "{}/:/tmp/";'''.format(self.sitePath) self.DeleteSite(get, multiple=1) del_successfully.append(get.webname) except: - del_failed[get.webname] = public.getMsg('DEL_ERROR1') + del_failed[get.webname] = public.get_msg_gettext('There was an error deleting, please try again.') pass public.serviceReload() - return {'status': True, 'msg': public.getMsg('DEL_WEBSITE_MULTIPLE', (','.join(del_successfully),)), + return {'status': True, 'msg': public.get_msg_gettext('Delete website [{}] successfully', (','.join(del_successfully),)), 'error': del_failed, 'success': del_successfully} @@ -756,7 +792,7 @@ set $bt_safe_open "{}/:/tmp/";'''.format(self.sitePath) def DeleteSite(self, get, multiple=None): proxyconf = self.__read_config(self.__proxyfile) id = get.id - if public.M('sites').where('id=?', (id,)).count() < 1: return public.returnMsg(False, 'SITE_NOT_EXIST') + if public.M('sites').where('id=?', (id,)).count() < 1: return public.return_msg_gettext(False, 'Specified site does NOT exist') siteName = get.webname get.siteName = siteName self.CloseTomcat(get) @@ -873,7 +909,8 @@ set $bt_safe_open "{}/:/tmp/";'''.format(self.sitePath) public.M('sites').where("id=?", (id,)).delete() public.M('binding').where("pid=?", (id,)).delete() public.M('domain').where("pid=?", (id,)).delete() - public.WriteLog('TYPE_SITE', "SITE_DEL_SUCCESS", (siteName,)) + public.M('wordpress_onekey').where("s_id=?", (id,)).delete() + public.write_log_gettext('Site manager', 'Successfully deleted site!', (siteName,)) # 是否删除关联数据库 if hasattr(get, 'database'): @@ -895,7 +932,7 @@ set $bt_safe_open "{}/:/tmp/";'''.format(self.sitePath) get.id = find['id'] ftp.ftp().DeleteUser(get) - return public.returnMsg(True, 'SITE_DEL_SUCCESS') + return public.return_msg_gettext(True, 'Successfully deleted site!') def _del_ols_listen_conf(self, sitename): conf_dir = '/www/server/panel/vhost/openlitespeed/listen/' @@ -996,11 +1033,11 @@ set $bt_safe_open "{}/:/tmp/";'''.format(self.sitePath) # 检查配置文件 isError = public.checkWebConfig() if isError != True: - return public.returnMsg(False, 'ERROR: %s

                                      ' % public.GetMsg( - "GET_ERR_IN_CONFILE") + isError.replace("\n", '
                                      ') + '
                                      ') + return public.return_msg_gettext(False, 'ERROR: %s

                                      ' % public.get_msg_gettext( + 'An error was detected in the configuration file. Please solve it before proceeding') + isError.replace("\n", '
                                      ') + '
                                      ') - if not 'domain' in get: return public.returnMsg(False, 'DOMAIN_NAME') - if len(get.domain) < 3: return public.returnMsg(False, 'SITE_ADD_DOMAIN_ERR_EMPTY') + if not 'domain' in get: return public.return_msg_gettext(False, 'Please enter the host domain name') + if len(get.domain) < 3: return public.return_msg_gettext(False, 'Domain cannot be empty!') domains = get.domain.replace(' ', '').split(',') for domain in domains: @@ -1009,23 +1046,25 @@ set $bt_safe_open "{}/:/tmp/";'''.format(self.sitePath) get.domain = self.ToPunycode(domain[0]).lower() get.port = '80' - reg = "^([\w\-\*]{1,100}\.){1,4}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$" - if not re.match(reg, get.domain): return public.returnMsg(False, 'SITE_ADD_DOMAIN_ERR_FORMAT') + reg = "^([\w\-\*]{1,100}\.){1,24}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$" + if not re.match(reg, get.domain): return public.return_msg_gettext(False, 'Format of domain is invalid!') if len(domain) == 2: get.port = domain[1] if get.port == "": get.port = "80" - if not public.checkPort(get.port): return public.returnMsg(False, 'SITE_ADD_DOMAIN_ERR_POER') + if not public.checkPort(get.port): return public.return_msg_gettext(False, 'Port range is incorrect! should be between 100-65535') # 检查域名是否存在 sql = public.M('domain') opid = sql.where("name=? AND (port=? OR pid=?)", (get.domain, get.port, get.id)).getField('pid') if opid: - if public.M('sites').where('id=?', (opid,)).count(): - return public.returnMsg(False, 'SITE_ADD_DOMAIN_ERR_EXISTS') + siteName = public.M('sites').where('id=?',(opid,)).getField('name') + if siteName: + return public.return_msg_gettext(False, 'The specified domain name has been bound by the website [{}]'.format(siteName)) sql.where('pid=?', (opid,)).delete() - - if public.M('binding').where('domain=?', (get.domain,)).count(): - return public.returnMsg(False, 'SITE_ADD_ERR_DOMAIN_EXISTS') + opid = public.M('binding').where('domain=?', (get.domain,)).getField('pid') + if opid: + siteName = public.M('sites').where('id=?',(opid,)).getField('name') + return public.returnMsg(False,'The specified domain name has been bound by a subdirectory of the website [{}]!'.format(siteName)) # 写配置文件 self.NginxDomain(get) @@ -1050,10 +1089,10 @@ set $bt_safe_open "{}/:/tmp/";'''.format(self.sitePath) if not multiple: public.serviceReload() public.check_domain_cloud(get.domain) - public.WriteLog('TYPE_SITE', 'DOMAIN_ADD_SUCCESS', (get.webname, get.domain)) + public.write_log_gettext('Site manager', 'Site [{}] added domain [{}] successfully!', (get.webname, get.domain)) sql.table('domain').add('pid,name,port,addtime', (get.id, get.domain, get.port, public.getDate())) - return public.returnMsg(True, 'SITE_ADD_DOMAIN') + return public.return_msg_gettext(True, 'Successfully added site!') # 判断ols_ssl是否已经设置 def _check_ols_ssl(self, webname): @@ -1211,7 +1250,7 @@ listener Default%s{ # rep = "php-cgi-([0-9]{2,3})\.sock" # version = re.search(rep,conf).groups()[0] version = public.get_php_version_conf(conf) - if len(version) < 2: return public.returnMsg(False, 'PHP_GET_ERR') + if len(version) < 2: return public.return_msg_gettext(False, 'Failed to get PHP version!') phpConfig = ''' #PHP @@ -1280,23 +1319,23 @@ listener Default%s{ del_successfully.append(tmp) except: tmp = get.domain + ':' + get.port - del_failed[tmp] = public.getMsg('DEL_ERROR1') + del_failed[tmp] = public.get_msg_gettext('There was an error deleting, please try again.') pass public.serviceReload() - return {'status': True, 'msg': public.getMsg('DEL_DOMAIN_MULTIPLE', (','.join(del_successfully),)), + return {'status': True, 'msg': public.get_msg_gettext('Delete domain [{}] successfully', (','.join(del_successfully),)), 'error': del_failed, 'success': del_successfully} # 删除域名 def DelDomain(self, get, multiple=None): - if not 'id' in get: return public.returnMsg(False, 'CHOOSE_DOMAIN') - if not 'port' in get: return public.returnMsg(False, 'CHOOSE_PORT') + if not 'id' in get: return public.return_msg_gettext(False, 'Please choose a domain name') + if not 'port' in get: return public.return_msg_gettext(False, 'Please choose a port') sql = public.M('domain') id = get['id'] port = get.port find = sql.where("pid=? AND name=?", (get.id, get.domain)).field('id,name').find() domain_count = sql.table('domain').where("pid=?", (id,)).count() - if domain_count == 1: return public.returnMsg(False, 'SITE_DEL_DOMAIN_ERR_ONLY') + if domain_count == 1: return public.return_msg_gettext(False, 'Last domain cannot be deleted!') # nginx file = self.setupPath + '/panel/vhost/nginx/' + get['webname'] + '.conf' @@ -1339,9 +1378,8 @@ listener Default%s{ newServerName = tmp.replace(' ' + get['domain'] + "\n", "\n") newServerName = newServerName.replace(' ' + get['domain'] + ' ', ' ') conf = conf.replace(tmp, newServerName) - # 保存配置 - public.writeFile(file, conf) + public.writeFile(file, conf.strip()) except: pass @@ -1349,10 +1387,10 @@ listener Default%s{ self._del_ols_domain(get) sql.table('domain').where("id=?", (find['id'],)).delete() - public.WriteLog('TYPE_SITE', 'DOMAIN_DEL_SUCCESS', (get.webname, get.domain)) + public.write_log_gettext('Site manager', 'Site [{}] deleted domain [{}] successfully!', (get.webname, get.domain)) if not multiple: public.serviceReload() - return public.returnMsg(True, 'DEL_SUCCESS') + return public.return_msg_gettext(True, 'Successfully deleted') # openlitespeed删除域名 def _del_ols_domain(self, get): @@ -1400,10 +1438,10 @@ listener Default%s{ csrpath = path + "/fullchain.pem" keypath = path + "/privkey.pem" - if (get.key.find('KEY') == -1): return public.returnMsg(False, 'SITE_SSL_ERR_PRIVATE') - if (get.csr.find('CERTIFICATE') == -1): return public.returnMsg(False, 'SITE_SSL_ERR_CERT') + if (get.key.find('KEY') == -1): return public.return_msg_gettext(False, 'Private Key ERROR, please check!') + if (get.csr.find('CERTIFICATE') == -1): return public.return_msg_gettext(False, 'Certificate ERROR, please check!') public.writeFile('/tmp/cert.pl', get.csr) - if not public.CheckCert('/tmp/cert.pl'): return public.returnMsg(False, 'CERT_ERR') + if not public.CheckCert('/tmp/cert.pl'): return public.return_msg_gettext(False, 'Error getting certificate') backup_cert = '/tmp/backup_cert_' + siteName import shutil @@ -1423,18 +1461,18 @@ listener Default%s{ if (type(isError) == str): if os.path.exists(path): shutil.rmtree(backup_cert) shutil.move(backup_cert, path) - return public.returnMsg(False, 'ERROR:
                                      ' + isError.replace("\n", '
                                      ') + '
                                      ') + return public.return_msg_gettext(False, 'ERROR:
                                      ' + isError.replace("\n", '
                                      ') + '
                                      ') public.serviceReload() if os.path.exists(path + '/partnerOrderId'): os.remove(path + '/partnerOrderId') if os.path.exists(path + '/certOrderId'): os.remove(path + '/certOrderId') p_file = '/etc/letsencrypt/live/' + get.siteName if os.path.exists(p_file): shutil.rmtree(p_file) - public.WriteLog('TYPE_SITE', 'SITE_SSL_SAVE_SUCCESS') + public.write_log_gettext('Site manager', 'Certificate saved!') # 清理备份证书 if os.path.exists(backup_cert): shutil.rmtree(backup_cert) - return public.returnMsg(True, 'SITE_SSL_SUCCESS') + return public.return_msg_gettext(True, 'Certificate saved!') # 获取运行目录 def GetRunPath(self, get): @@ -1455,7 +1493,7 @@ listener Default%s{ domains = json.loads(get.domains) if not len(domains): - return public.returnMsg(False, 'CHOOSE_DOMAIN') + return public.return_msg_gettext(False, 'Please choose a domain name') file_auth = True if hasattr(get, 'dnsapi'): @@ -1477,12 +1515,12 @@ listener Default%s{ for domain in domains: if public.checkIp(domain): continue if domain.find('*.') >= 0 and file_auth: - return public.returnMsg(False, 'SSL_FILE_V_ERR') + return public.return_msg_gettext(False, 'A generic domain name cannot be used to apply for a certificate using [File Validation]!') if file_auth: get.sitename = get.siteName - if self.GetRedirectList(get): return public.returnMsg(False, 'SITE_SSL_ERR_301') - if self.GetProxyList(get): return public.returnMsg(False, 'SSL_FILE_V_ERR_PROXY') + if self.GetRedirectList(get): return public.return_msg_gettext(False, 'Your site has 301 Redirect on,Please turn it off first!') + if self.GetProxyList(get): return public.return_msg_gettext(False, 'Sites that have reverse proxy turned on cannot request SSL!') data = self.get_site_info(get.siteName) get.id = data['id'] runPath = self.GetRunPath(get) @@ -1504,10 +1542,10 @@ listener Default%s{ get.dns_param = '|'.join(param) n_list = ['dns', 'dns_bt'] if not get.dnsapi in n_list: - if len(get.dns_param) < 16: return public.returnMsg(False, 'API_ERR', (get.dnsapi,)) + if len(get.dns_param) < 16: return public.return_msg_gettext(False, 'No valid DNSAPI key information found', (get.dnsapi,)) if get.dnsapi == 'dns_bt': if not os.path.exists('plugin/dns/dns_main.py'): - return public.returnMsg(False, 'CLOUD_DNS_ERR') + return public.return_msg_gettext(False, 'Please go to the software store to install [Cloud Resolution] and complete the domain name NS binding.') self.check_ssl_pack() try: @@ -1516,8 +1554,8 @@ listener Default%s{ except Exception as ex: if str(ex).find('No module named requests') != -1: public.ExecShell("pip install requests &") - return public.returnMsg(False, 'REQUEST_MODULE_ERR') - return public.returnMsg(False, str(ex)) + return public.return_msg_gettext(False, 'Missing requests component, please try to repair the panel!') + return public.return_msg_gettext(False, str(ex)) lets = panelLets.panelLets() result = lets.apple_lest_cert(get) @@ -1537,11 +1575,11 @@ listener Default%s{ try: import requests except: - public.ExecShell('pip install requests') + public.ExecShell('btpip install requests') try: import OpenSSL except: - public.ExecShell('pip install pyopenssl') + public.ExecShell('btpip install pyOpenSSL') # 判断DNS-API是否设置 def Check_DnsApi(self, dnsapi): @@ -1605,7 +1643,7 @@ listener Default%s{ is_write = True if is_write: public.writeFile('./config/dns_api.json', json.dumps(apis)) - return public.returnMsg(True, "SET_SUCCESS") + return public.return_msg_gettext(True, 'Setup successfully!') # 获取站点所有域名 def GetSiteDomains(self, get): @@ -1775,7 +1813,7 @@ listener SSL443 { """ % (get.first_domain, get.first_domain, self.get_tls13()) if (conf.find('ssl_certificate') != -1): public.serviceReload() - return public.returnMsg(True, 'SITE_SSL_OPEN_SUCCESS') + return public.return_msg_gettext(True, 'SSL turned on!') conf = conf.replace('#error_page 404/404.html;', sslStr) # 添加端口 @@ -1825,7 +1863,7 @@ listener SSL443 { # rep = r"php-cgi-([0-9]{2,3})\.sock" # version = re.search(rep, conf).groups()[0] version = public.get_php_version_conf(conf) - if len(version) < 2: return public.returnMsg(False, 'PHP_GET_ERR') + if len(version) < 2: return public.return_msg_gettext(False, 'Failed to get PHP version!') phpConfig = ''' #PHP @@ -1882,8 +1920,8 @@ listener SSL443 { if os.path.exists(self.nginx_conf_bak): shutil.copyfile(self.nginx_conf_bak, ng_file) if os.path.exists(self.apache_conf_bak): shutil.copyfile(self.apache_conf_bak, file) public.ExecShell("rm -f /tmp/backup_*.conf") - return public.returnMsg(False, - public.GetMsg("SSL_ERR_CERT") + ':
                                      ' + isError.replace( + return public.return_msg_gettext(False, + public.get_msg_gettext('Certificate ERROR, please check!') + ':
                                      ' + isError.replace( "\n", '
                                      ') + '
                                      ') sql = public.M('firewall') @@ -1894,8 +1932,8 @@ listener SSL443 { firewalls.firewalls().AddAcceptPort(get) public.serviceReload() self.save_cert(get) - public.WriteLog('TYPE_SITE', 'SITE_SSL_OPEN_SUCCESS', (siteName,)) - result = public.returnMsg(True, 'SITE_SSL_OPEN_SUCCESS') + public.write_log_gettext('Site manager', 'Site [{}] turned on SSL successfully!', (siteName,)) + result = public.return_msg_gettext(True, 'SSL turned on!') result['csr'] = public.readFile('/www/server/panel/vhost/cert/' + get.siteName + '/fullchain.pem') result['key'] = public.readFile('/www/server/panel/vhost/cert/' + get.siteName + '/privkey.pem') return result @@ -1920,7 +1958,7 @@ listener SSL443 { file = self.setupPath + '/panel/vhost/nginx/node_'+siteName+'.conf' conf = public.readFile(file) if conf: - if conf.find('ssl_certificate') == -1: return public.returnMsg(False, 'NOT_OPEN_SSL') + if conf.find('ssl_certificate') == -1: return public.return_msg_gettext(False, 'SSL is NOT currently enabled') to = """#error_page 404/404.html; #HTTP_TO_HTTPS_START if ($server_port !~ 443){ @@ -1960,7 +1998,7 @@ listener SSL443 { #HTTP_TO_HTTPS_END''' public.writeFile(file, ols_force_https) public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') # CloseToHttps def CloseToHttps(self, get): @@ -1985,7 +2023,7 @@ listener SSL443 { public.ExecShell('rm -f {}*'.format(file)) public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') # 是否跳转到https def IsToHttps(self, siteName): @@ -2077,15 +2115,15 @@ listener SSL443 { p_file = '/etc/letsencrypt/live/' + siteName + '/partnerOrderId' if os.path.exists(p_file): public.ExecShell('rm -f ' + p_file) - public.WriteLog('TYPE_SITE', 'SITE_SSL_CLOSE_SUCCESS', (siteName,)) + public.write_log_gettext('Site manager', 'Site [{}] turned off SSL successfully!', (siteName,)) public.serviceReload() - return public.returnMsg(True, 'SITE_SSL_CLOSE_SUCCESS') + return public.return_msg_gettext(True, 'SSL turned off!') def _del_ols_443_domain(self, sitename): file = "/www/server/panel/vhost/openlitespeed/listen/443.conf" conf = public.readFile(file) if conf: - rep = '\n\s*map\s*{}'.format(sitename) + rep = '\n\s*map\s*{}.*'.format(sitename) conf = re.sub(rep, '', conf) if not "map " in conf: public.ExecShell('rm -f {}*'.format(file)) @@ -2114,7 +2152,7 @@ listener SSL443 { if public.get_webserver() == "openlitespeed": file = self.setupPath + '/panel/vhost/' + public.get_webserver() + '/detail/' + siteName + '.conf' conf = public.readFile(file) - if not conf: return public.returnMsg(False, 'WEBSITE_CONF_NOT_EXIST') + if not conf: return public.return_msg_gettext(False, 'The specified website profile does not exist') if public.get_webserver() == 'nginx': keyText = 'ssl_certificate' @@ -2185,10 +2223,10 @@ listener SSL443 { self.SiteStop(get, multiple=1) public.serviceReload() if get.status == '1': - return {'status': True, 'msg': public.getMsg('ENABLE_WEBSITE_MULTIPLE', (','.join(sites_name),)), + return {'status': True, 'msg': public.get_msg_gettext('Enable website [{}] successfully', (','.join(sites_name),)), 'error': {}, 'success': sites_name} else: - return {'status': True, 'msg': public.getMsg('DISABLE_WEBSITE_MULTIPLE', (','.join(sites_name),)), + return {'status': True, 'msg': public.get_msg_gettext('Disable website [{}] successfully', (','.join(sites_name),)), 'error': {}, 'success': sites_name} # 启动站点 @@ -2224,8 +2262,8 @@ listener SSL443 { public.M('sites').where("id=?", (id,)).setField('status', '1') if not multiple: public.serviceReload() - public.WriteLog('TYPE_SITE', 'SITE_START_SUCCESS', (get.name,)) - return public.returnMsg(True, 'SITE_START_SUCCESS') + public.write_log_gettext('Site manager', 'Site [{}] started!', (get.name,)) + return public.return_msg_gettext(True, 'Site started') def _process_has_run_dir(self, website_name, website_path, stop_path): ''' @@ -2248,9 +2286,12 @@ listener SSL443 { def SiteStop(self, get, multiple=None): path = self.setupPath + '/stop' id = get.id + site_status = public.M('sites').where("id=?", (id,)).getField('status') + if str(site_status) != '1': + return public.returnMsg(True, 'SITE_STOP_SUCCESS') if not os.path.exists(path): os.makedirs(path) - public.downloadFile('http://download.bt.cn/stop_en.html', path + '/index.html') + public.downloadFile('https://node.aapanel.com/stop_en.html', path + '/index.html') # if 'This site has been closed by administrator' not in public.readFile(path + '/index.html'): # public.downloadFile('http://download.bt.cn/stop_en.html', path + '/index.html') @@ -2296,8 +2337,8 @@ listener SSL443 { public.M('sites').where("id=?", (id,)).setField('status', '0') if not multiple: public.serviceReload() - public.WriteLog('TYPE_SITE', 'SITE_STOP_SUCCESS', (get.name,)) - return public.returnMsg(True, 'SITE_STOP_SUCCESS') + public.write_log_gettext('Site manager', 'Site [{}] stopped!', (get.name,)) + return public.return_msg_gettext(True, 'Site stopped') # 取流量限制值 def GetLimitNet(self, get): @@ -2333,11 +2374,11 @@ listener SSL443 { # 设置流量限制 def SetLimitNet(self, get): - if (public.get_webserver() != 'nginx'): return public.returnMsg(False, 'SITE_NETLIMIT_ERR') + if (public.get_webserver() != 'nginx'): return public.return_msg_gettext(False, 'Site Traffic Control only supports Nginx Web Server!') id = get.id if int(get.perserver) < 1 or int(get.perip) < 1 or int(get.perip) < 1: - return public.returnMsg(False, 'WEBSITE_TRAFFIC_LIMIT_ERR') + return public.return_msg_gettext(False, 'Concurrency restrictions, IP restrictions, traffic restrictions must be greater than 0') perserver = 'limit_conn perserver ' + get.perserver + ';' perip = 'limit_conn perip ' + get.perip + ';' limit_rate = 'limit_rate ' + get.limit_rate + 'k;' @@ -2378,11 +2419,11 @@ listener SSL443 { isError = public.checkWebConfig() if (isError != True): if os.path.exists(self.nginx_conf_bak): shutil.copyfile(self.nginx_conf_bak, filename) - return public.returnMsg(False, 'ERROR:
                                      ' + isError.replace("\n", '
                                      ') + '
                                      ') + return public.return_msg_gettext(False, 'ERROR:
                                      ' + isError.replace("\n", '
                                      ') + '
                                      ') public.serviceReload() - public.WriteLog('TYPE_SITE', 'SITE_NETLIMIT_OPEN_SUCCESS', (siteName,)) - return public.returnMsg(True, 'SET_SUCCESS') + public.write_log_gettext('Site manager', 'Site [{}] traffic control turned on!', (siteName,)) + return public.return_msg_gettext(True, 'Setup successfully!') # 关闭流量限制 def CloseLimitNet(self, get): @@ -2404,8 +2445,8 @@ listener SSL443 { conf = re.sub(rep, '', conf) public.writeFile(filename, conf) public.serviceReload() - public.WriteLog('TYPE_SITE', 'SITE_NETLIMIT_CLOSE_SUCCESS', (siteName,)) - return public.returnMsg(True, 'SITE_NETLIMIT_CLOSE_SUCCESS') + public.write_log_gettext('Site manager', 'Site Traffic Control has been turned off!', (siteName,)) + return public.return_msg_gettext(True, 'Site Traffic Control has been turned off!') # 取301配置状态 def Get301Status(self, get): @@ -2484,12 +2525,12 @@ listener SSL443 { toDomain = get.toDomain type = get.type rep = "(http|https)\://.+" - if not re.match(rep, toDomain): return public.returnMsg(False, "SITE_PROXY_ERR_URL") + if not re.match(rep, toDomain): return public.return_msg_gettext(False, 'URL address is invalid!') # nginx filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' mconf = public.readFile(filename) - if mconf == False: return public.returnMsg(False, 'CONF_FILE_NOT_EXISTS') + if mconf == False: return public.return_msg_gettext(False, 'Configuration file not exist') if (srcDomain == 'all'): conf301 = "\t#301-START\n\t\treturn 301 " + toDomain + "$request_uri;\n\t#301-END" else: @@ -2504,7 +2545,7 @@ listener SSL443 { # apache filename = self.setupPath + '/panel/vhost/apache/' + siteName + '.conf' mconf = public.readFile(filename) - if mconf == False: return public.returnMsg(False, 'CONF_FILE_NOT_EXISTS') + if mconf == False: return public.return_msg_gettext(False, 'Configuration file not exist') if type == '1': if (srcDomain == 'all'): conf301 = "\n\t#301-START\n\t\n\t\tRewriteEngine on\n\t\tRewriteRule ^(.*)$ " + toDomain + "$1 [L,R=301]\n\t\n\t#301-END\n" @@ -2535,10 +2576,10 @@ listener SSL443 { isError = public.checkWebConfig() if (isError != True): - return public.returnMsg(False, 'ERROR:
                                      ' + isError.replace("\n", '
                                      ') + '
                                      ') + return public.return_msg_gettext(False, 'ERROR:
                                      ' + isError.replace("\n", '
                                      ') + '
                                      ') public.serviceReload() - return public.returnMsg(True, 'SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') # 取子目录绑定 def GetDirBinding(self, get): @@ -2556,26 +2597,38 @@ listener SSL443 { get.path = path self.SetDirUserINI(get) siteName = public.M('sites').where('id=?', (get.id,)).getField('name') - public.WriteLog('TYPE_SITE', "SITE_DIR_NOT_EXIST_RECREATE", (siteName, path)) + public.write_log_gettext('Site manager', "Site [{}], document root [{}] does NOT exist, recreated!", (siteName, path)) dirnames = [] - for filename in os.listdir(path): - try: - json.dumps(filename) - if sys.version_info[0] == 2: - filename = filename.encode('utf-8') - else: - filename.encode('utf-8') - filePath = path + '/' + filename - if os.path.islink(filePath): continue - if os.path.isdir(filePath): - dirnames.append(filename) - except: - pass + # 取运行目录 + run_path = self.GetRunPath(get) + if run_path: path += run_path + + # 遍历目录 + if os.path.exists(path): + for filename in os.listdir(path): + try: + json.dumps(filename) + if sys.version_info[0] == 2: + filename = filename.encode('utf-8') + else: + filename.encode('utf-8') + filePath = path + '/' + filename + if os.path.islink(filePath): continue + if os.path.isdir(filePath): + dirnames.append(filename) + except: + pass data = {} + data['run_path'] = run_path # 运行目录 data['dirs'] = dirnames - data['binding'] = public.M('binding').where('pid=?', (get.id,)).field( - 'id,pid,domain,path,port,addtime').select() + data['binding'] = public.M('binding').where('pid=?',(get.id,)).field('id,pid,domain,path,port,addtime').select() + + # 标记子目录是否存在 + for dname in data['binding']: + _path = os.path.join(path,dname['path']) + if not os.path.exists(_path): + dname['path'] += ' >> error: directory does not exist' return data # 添加子目录绑定 @@ -2587,18 +2640,26 @@ listener SSL443 { port = '80' version = '' if len(tmp) > 1: port = tmp[1] - if not hasattr(get, 'dirName'): public.returnMsg(False, 'DIR_EMPTY') + if not hasattr(get, 'dirName'): public.return_msg_gettext(False, 'Directory cannot be empty!') dirName = get.dirName reg = "^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$" - if not re.match(reg, domain): return public.returnMsg(False, 'SITE_ADD_ERR_DOMAIN') + if not re.match(reg, domain): return public.return_msg_gettext(False, 'Format of primary domain is incorrect') + + siteInfo = public.M('sites').where("id=?",(id,)).field('id,path,name').find() + # 实际运行目录 + root_path = siteInfo['path'] + run_path = self.GetRunPath(get) + if run_path: root_path += run_path + + + webdir = root_path + '/' + dirName + webdir = webdir.replace('//','/').strip() - siteInfo = public.M('sites').where("id=?", (id,)).field('id,path,name').find() - webdir = siteInfo['path'] + '/' + dirName sql = public.M('binding') - if sql.where("domain=?", (domain,)).count() > 0: return public.returnMsg(False, 'SITE_ADD_ERR_DOMAIN_EXISTS') - if public.M('domain').where("name=?", (domain,)).count() > 0: return public.returnMsg(False, - 'SITE_ADD_ERR_DOMAIN_EXISTS') + if sql.where("domain=?", (domain,)).count() > 0: return public.return_msg_gettext(False, 'The domain you tried to add already exists!') + if public.M('domain').where("name=?", (domain,)).count() > 0: return public.return_msg_gettext(False, + 'The domain you tried to add already exists!') filename = self.setupPath + '/panel/vhost/nginx/' + siteInfo['name'] + '.conf' nginx_conf_file = filename @@ -2606,8 +2667,15 @@ listener SSL443 { if conf: listen_ipv6 = '' if self.is_ipv6: listen_ipv6 = "\n listen [::]:%s;" % port - rep = "enable-php-(\w{2,5})\.conf" - tmp = re.search(rep,conf).groups() + try: + rep = "enable-php-(\w{2,5})\.conf" + tmp = re.search(rep,conf) + if not tmp: + rep = "enable-php-(\d+-wpfastcgi).conf" + tmp = re.search(rep, conf) + except: + return public.returnMsg(False,"Get enable php config failed!") + tmp = tmp.groups() version = tmp[0] bindingConf = ''' #BINDING-%s-START @@ -2647,7 +2715,7 @@ server error_log %s.error.log; } #BINDING-%s-END''' % (domain, port, listen_ipv6, domain, webdir, version, self.setupPath, siteInfo['name'], - public.GetMsg("NGINX_CONF_BAN_FILE_DIR"), public.GetMsg("NGINX_CONF_ONECLICK_SET_SSL_DIR"), + public.get_msg_gettext('# Forbidden files or directories'), public.get_msg_gettext('# Directory verification related settings for one-click application for SSL certificate'), public.GetConfigValue('logs_path') + '/' + siteInfo['name'], public.GetConfigValue('logs_path') + '/' + siteInfo['name'], domain) @@ -2732,12 +2800,12 @@ server if isError != True: if os.path.exists(self.nginx_conf_bak): shutil.copyfile(self.nginx_conf_bak, nginx_conf_file) if os.path.exists(self.apache_conf_bak): shutil.copyfile(self.apache_conf_bak, filename) - return public.returnMsg(False, 'ERROR:
                                      ' + isError.replace("\n", '
                                      ') + '
                                      ') + return public.return_msg_gettext(False, 'ERROR:
                                      ' + isError.replace("\n", '
                                      ') + '
                                      ') public.M('binding').add('pid,domain,port,path,addtime', (id, domain, port, dirName, public.getDate())) public.serviceReload() - public.WriteLog('TYPE_SITE', 'SITE_BINDING_ADD_SUCCESS', (siteInfo['name'], dirName, domain)) - return public.returnMsg(True, 'ADD_SUCCESS') + public.write_log_gettext('Site manager', 'Site [{}] subdirectory [{}] bound to [{}]', (siteInfo['name'], dirName, domain)) + return public.return_msg_gettext(True, 'Successfully added') def delete_dir_bind_multiple(self, get): ''' @@ -2757,10 +2825,10 @@ server self.DelDirBinding(get, multiple=1) del_successfully.append(domain) except: - del_failed[domain] = public.getMsg('DEL_ERROR1') + del_failed[domain] = public.get_msg_gettext('There was an error deleting, please try again.') pass public.serviceReload() - return {'status': True, 'msg': public.getMsg('DEL_SUBDIRBIND', (','.join(del_successfully),)), + return {'status': True, 'msg': public.get_msg_gettext('Delete [{}] subdirectory binding successfully', (','.join(del_successfully),)), 'error': del_failed, 'success': del_successfully} @@ -2809,13 +2877,18 @@ server detail_file = "{}/panel/vhost/openlitespeed/detail/{}_{}.conf".format(self.setupPath, siteName, binding['path']) public.ExecShell("rm -f {}*".format(detail_file)) - public.M('binding').where("id=?", (id,)).delete() - filename = self.setupPath + '/panel/vhost/rewrite/' + siteName + '_' + binding['path'] + '.conf' - if os.path.exists(filename): public.ExecShell('rm -rf %s' % filename) + # 从数据库删除绑定 + public.M('binding').where("id=?",(id,)).delete() + + # 如果没有其它域名绑定同一子目录,则删除该子目录的伪静态规则 + if not public.M('binding').where("path=? AND pid=?",(binding['path'],binding['pid'])).count(): + filename = self.setupPath + '/panel/vhost/rewrite/' + siteName + '_' + binding['path'] + '.conf' + if os.path.exists(filename): public.ExecShell('rm -rf %s'%filename) + # 是否需要重载服务 if not multiple: public.serviceReload() - public.WriteLog('TYPE_SITE', 'SITE_BINDING_DEL_SUCCESS', (siteName, binding['path'])) - return public.returnMsg(True, 'DEL_SUCCESS') + public.write_log_gettext('Site manager', 'Deleted site [{}] subdirectory [{}] binding', (siteName, binding['path'])) + return public.return_msg_gettext(True, 'Successfully deleted') # 取子目录Rewrite def GetDirRewrite(self, get): @@ -2863,7 +2936,7 @@ server if public.get_webserver() == 'openlitespeed': file = self.setupPath + '/panel/vhost/' + public.get_webserver() + '/detail/' + Name + '.conf' conf = public.readFile(file) - if conf == False: return public.returnMsg(False, 'CONF_FILE_NOT_EXISTS') + if conf == False: return public.return_msg_gettext(False, 'Configuration file not exist') if public.get_webserver() == 'nginx': rep = "\s+index\s+(.+);" elif public.get_webserver() == 'apache': @@ -2875,17 +2948,18 @@ server if public.get_webserver() == 'openlitespeed': return tmp[0] return tmp[0].replace(' ', ',') - return public.returnMsg(False, 'INDEX_FILE_ERR') + return public.return_msg_gettext(False, 'Failed to get, there is no default document in the configuration file') # 设置默认文档 def SetIndex(self, get): id = get.id - if get.Index.find('.') == -1: return public.returnMsg(False, 'SITE_INDEX_ERR_FORMAT') Index = get.Index.replace(' ', '') - Index = get.Index.replace(',,', ',') + Index = Index.replace(',,', ',').strip() + if not Index: return public.returnMsg(False,"Default index file cannot be empty") + if get.Index.find('.') == -1: return public.return_msg_gettext(False, 'Default Document Format is invalid, e.g., index.html') - if len(Index) < 3: return public.returnMsg(False, 'SITE_INDEX_ERR_EMPTY') + if len(Index) < 3: return public.return_msg_gettext(False, 'Default Document cannot be empty!') Name = public.M('sites').where("id=?", (id,)).getField('name') # 准备指令 @@ -2919,22 +2993,22 @@ server public.writeFile(file, conf) public.serviceReload() - public.WriteLog('TYPE_SITE', 'SITE_INDEX_SUCCESS', (Name, Index_L)) - return public.returnMsg(True, 'SET_SUCCESS') + public.write_log_gettext('Site manager', 'Defualt document of site [{}] is [{}]', (Name, Index_L)) + return public.return_msg_gettext(True, 'Setup successfully!') # 修改物理路径 def SetPath(self, get): id = get.id Path = self.GetPath(get.path) - if Path == "" or id == '0': return public.returnMsg(False, "DIR_EMPTY") + if Path == "" or id == '0': return public.return_msg_gettext(False, 'Directory cannot be empty!') - if not self.__check_site_path(Path): return public.returnMsg(False,"PATH_ERROR") + if not self.__check_site_path(Path): return public.return_msg_gettext(False,'System critical directory cannot be used as site directory') if not public.check_site_path(Path): a, c = public.get_sys_path() - return public.returnMsg(False,'Please do not set the website root directory to the system main directory:
                                      {}'.format("
                                      ".join(a+c))) + return public.return_msg_gettext(False,'Please do not set the website root directory to the system main directory:
                                      {}'.format("
                                      ".join(a+c))) SiteFind = public.M("sites").where("id=?", (id,)).field('path,name').find() - if SiteFind["path"] == Path: return public.returnMsg(False, "SITE_PATH_ERR_RE") + if SiteFind["path"] == Path: return public.return_msg_gettext(False, 'Same as original path, no need to change!') Name = SiteFind['name'] file = self.setupPath + '/panel/vhost/nginx/' + Name + '.conf' conf = public.readFile(file) @@ -2970,18 +3044,22 @@ server public.serviceReload() public.M("sites").where("id=?",(id,)).setField('path',Path) - public.WriteLog('TYPE_SITE', 'SITE_PATH_SUCCESS',(Name,)) - return public.returnMsg(True, "SET_SUCCESS") + public.write_log_gettext('Site manager', 'Successfully changed directory of site [{}]!',(Name,)) + return public.return_msg_gettext(True, 'Successfully set') #取当前可用PHP版本 def GetPHPVersion(self,get): - phpVersions = ('00','other','52','53','54','55','56','70','71','72','73','74','80','81') + + phpVersions = public.get_php_versions() + phpVersions.insert(0,'other') + phpVersions.insert(0,'00') httpdVersion = "" filename = self.setupPath + '/apache/version.pl' if os.path.exists(filename): httpdVersion = public.readFile(filename).strip() if httpdVersion == '2.2': phpVersions = ('00','52','53','54') - if httpdVersion == '2.4': phpVersions = ('00','other','53','54','55','56','70','71','72','73','74','80','81') + if httpdVersion == '2.4': + if '52' in phpVersions: phpVersions.remove('52') if os.path.exists('/www/server/nginx/sbin/nginx'): cfile = '/www/server/nginx/conf/enable-php-00.conf' if not os.path.exists(cfile): public.writeFile(cfile,'') @@ -2997,7 +3075,7 @@ server tmp['version'] = val tmp['name'] = 'PHP-'+val if val == '00': - tmp['name'] = public.getMsg('STATIC') + tmp['name'] = public.get_msg_gettext('Static') if val == 'other': if s_type: @@ -3025,7 +3103,7 @@ server data['php_other'] = re.findall(r"fastcgi_pass\s+(.+);",conf)[0] return data except: - return public.returnMsg(False, 'SITE_PHPVERSION_ERR_A22,{}'.format(public.get_error_info())) + return public.return_msg_gettext(False, 'Apache2.2 does NOT support MultiPHP!,{}'.format(public.get_error_info())) def set_site_php_version_multiple(self, get): ''' @@ -3049,10 +3127,10 @@ server continue set_phpv_successfully.append(get.siteName) except: - set_phpv_failed[get.siteName] = public.getMsg('SET_ERROR1') + set_phpv_failed[get.siteName] = public.get_msg_gettext('There was an error setting, please try again.') pass public.serviceReload() - return {'status': True, 'msg': public.getMsg('SET_PHPV_MULTIPLE', (','.join(set_phpv_successfully),)), + return {'status': True, 'msg': public.get_msg_gettext('Set up website [{}] PHP version successfully', (','.join(set_phpv_successfully),)), 'error': set_phpv_failed, 'success': set_phpv_successfully} @@ -3061,12 +3139,15 @@ server siteName = get.siteName version = get.version if version == 'other' and not public.get_webserver() in ['nginx','tengine']: - return public.returnMsg(False,'Custom PHP configuration only supports Nginx') + return public.return_msg_gettext(False,'Custom PHP configuration only supports Nginx') try: # nginx file = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' conf = public.readFile(file) if conf: + wp00 = "/www/server/nginx/conf/enable-php-00-wpfastcgi.conf" + if not os.path.exists(wp00): + public.writeFile(wp00, '') other_path = '/www/server/panel/vhost/other_php/{}'.format(siteName) if not os.path.exists(other_path): os.makedirs(other_path) other_rep = "{}/enable-php-other.conf".format(other_path) @@ -3077,18 +3158,18 @@ server get.other = get.other.strip() if not get.other: - return public.returnMsg(False,'The PHP connection configuration cannot be empty when customizing the version!') + return public.return_msg_gettext(False,'The PHP connection configuration cannot be empty when customizing the version!') if not re.match(r"^(\d+\.\d+\.\d+\.\d+:\d+|unix:[\w/\.-]+)$",get.other): - return public.returnMsg(False,'The PHP connection configuration format is incorrect, please refer to the example!') + return public.return_msg_gettext(False,'The PHP connection configuration format is incorrect, please refer to the example!') other_tmp = get.other.split(':') if other_tmp[0] == 'unix': if not os.path.exists(other_tmp[1]): - return public.returnMsg(False,'The specified unix socket [{}] does not exist!'.format(other_tmp[1])) + return public.return_msg_gettext(False,'The specified unix socket [{}] does not exist!'.format(other_tmp[1])) else: if not public.check_tcp(other_tmp[0],int(other_tmp[1])): - return public.returnMsg(False,'Unable to connect to [{}], please check whether the machine can connect to the target server'.format(get.other)) + return public.return_msg_gettext(False,'Unable to connect to [{}], please check whether the machine can connect to the target server'.format(get.other)) other_conf = '''location ~ [^/]\.php(/|$) {{ @@ -3103,13 +3184,18 @@ server rep = "include\s+enable-php-(\w{2,5})\.conf" tmp = re.search(rep,conf) if tmp: conf = conf.replace(tmp.group(),'include ' + dst) + elif re.search("enable-php-\d+-wpfastcgi.conf",conf): + dst = 'enable-php-{}-wpfastcgi.conf'.format(version) + conf = conf.replace(other_rep,dst) + rep = "enable-php-\d+-wpfastcgi.conf" + tmp = re.search(rep, conf) + if tmp:conf = conf.replace(tmp.group(),dst) else: dst = 'enable-php-'+version+'.conf' conf = conf.replace(other_rep,dst) rep = "enable-php-(\w{2,5})\.conf" tmp = re.search(rep,conf) if tmp: conf = conf.replace(tmp.group(),dst) - public.writeFile(file,conf) try: import site_dir_auth @@ -3145,16 +3231,16 @@ server public.writeFile(file, conf) if not multiple: public.serviceReload() - public.WriteLog("TYPE_SITE", "SITE_PHPVERSION_SUCCESS", (siteName, version)) - return public.returnMsg(True, 'SITE_PHPVERSION_SUCCESS', (siteName, version)) + public.write_log_gettext("Site manager", 'Successfully changed PHP Version of site [{}] to PHP-{}', (siteName, version)) + return public.return_msg_gettext(True, 'Successfully changed PHP Version of site [{}] to PHP-{}', (siteName, version)) except: return public.get_error_info() - return public.returnMsg(False, 'PHP_SETUP_FAILED') + return public.return_msg_gettext(False, 'Setup failed, no enable-php-xx related configuration items were found in the website configuration file!') # 是否开启目录防御 def GetDirUserINI(self, get): path = get.path + self.GetRunPath(get) - if not path: return public.returnMsg(False, 'DIR_NOT_EXISTS') + if not path: return public.return_msg_gettext(False, 'Requested directory does not exist') id = get.id get.name = public.M('sites').where("id=?", (id,)).getField('name') data = {} @@ -3210,7 +3296,7 @@ server public.writeFile(filename, conf) public.ExecShell("chattr +i " + filename) public.set_site_open_basedir_nginx(siteName) - return public.returnMsg(True, 'SITE_BASEDIR_CLOSE_SUCCESS') + return public.return_msg_gettext(True, 'Base directory turned off!') if conf and "session.save_path" in conf: rep = "session.save_path\s*=\s*(.*)" @@ -3221,7 +3307,7 @@ server public.ExecShell("chattr +i " + filename) public.set_site_open_basedir_nginx(siteName) public.serviceReload() - return public.returnMsg(True, 'SITE_BASEDIR_OPEN_SUCCESS') + return public.return_msg_gettext(True, 'Base directory turned on!') except Exception as e: public.ExecShell("chattr +i " + filename) return str(e) @@ -3292,7 +3378,7 @@ server else: get.todomain = "" get.proxysite = str(re.search(url_rep, old_conf).group(2)) - get.proxyname = public.GetMsg("OLD_PROXY") + get.proxyname = public.get_msg_gettext('Old proxy') get.type = 1 get.proxydir = "/" get.advanced = 0 @@ -3344,9 +3430,9 @@ server del_failed[proxyname] = resule['msg'] del_successfully.append(proxyname) except: - del_failed[proxyname] = public.getMsg('DEL_ERROR1') + del_failed[proxyname] = public.get_msg_gettext('There was an error deleting, please try again.') pass - return {'status': True, 'msg': public.getMsg('DEL_PROXY_MULTIPLE', (','.join(del_failed),)), + return {'status': True, 'msg': public.get_msg_gettext('Delete [ {} ] proxy successfully', (','.join(del_failed),)), 'error': del_failed, 'success': del_successfully} @@ -3375,7 +3461,7 @@ server self.SetApache(get.sitename) if not multiple: public.serviceReload() - return public.returnMsg(True, 'DEL_SUCCESS') + return public.return_msg_gettext(True, 'Successfully deleted') # 检查代理是否存在 def __check_even(self, get, action=""): @@ -3429,31 +3515,31 @@ server else: sk.connect((d, 443)) except: - return public.returnMsg(False, "CANT_GET_URL") + return public.return_msg_gettext(False, 'Can NOT get target URL') # 基本设置检查 def __CheckStart(self, get, action=""): isError = public.checkWebConfig() if (isError != True): - return public.returnMsg(False, 'GET_ERR_IN_CONFILE') + return public.return_msg_gettext(False, 'An error was detected in the configuration file. Please solve it before proceeding') if action == "create": if sys.version_info.major < 3: - if len(get.proxyname) < 3 or len(get.proxyname) > 15: - return public.returnMsg(False, 'NAME_LEN') + if len(get.proxyname) < 3 or len(get.proxyname) > 40: + return public.return_msg_gettext(False, 'Database name cannot be more than 40 characters!') else: - if len(get.proxyname.encode("utf-8")) < 3 or len(get.proxyname.encode("utf-8")) > 15: - return public.returnMsg(False, 'NAME_LEN') + if len(get.proxyname.encode("utf-8")) < 3 or len(get.proxyname.encode("utf-8")) > 40: + return public.return_msg_gettext(False, 'Database name cannot be more than 40 characters!') if self.__check_even(get, action): - return public.returnMsg(False, 'PROXY_NAME_OR_DIR_EXIST') + return public.return_msg_gettext(False, 'Specified reverse proxy name or proxy folder already exists') # 判断代理,只能有全局代理或目录代理 if self.__check_proxy_even(get, action): - return public.returnMsg(False, 'CANT_SET_DIRANDGLOBA') + return public.return_msg_gettext(False, 'Cannot set both directory and global proxies') # 判断cachetime类型 if get.cachetime: try: int(get.cachetime) except: - return public.returnMsg(False, "INPUT_NUM") + return public.return_msg_gettext(False, 'Please enter number') rep = "http(s)?\:\/\/" # repd = "http(s)?\:\/\/([a-zA-Z0-9][-a-zA-Z0-9]{0,62}\.)+([a-zA-Z0-9][a-zA-Z0-9]{0,62})+.?" @@ -3461,19 +3547,19 @@ server repte = "[\?\=\[\]\)\(\*\&\^\%\$\#\@\!\~\`{\}\>\<\,\',\"]+" # 检测代理目录格式 if re.search(repte, get.proxydir): - return public.returnMsg(False, "PROXY_DIR_ERR", ("?,=,[,],),(,*,&,^,%,$,#,@,!,~,`,{,},>,<,\,',\"]",)) + return public.return_msg_gettext(False, "PROXY_DIR_ERR", ("?,=,[,],),(,*,&,^,%,$,#,@,!,~,`,{,},>,<,\,',\"]",)) # 检测发送域名格式 if get.todomain: if re.search("[\}\{\#\;\"\']+",get.todomain): - return public.returnMsg(False, 'Sent Domain format error :'+get.todomain+'
                                      The following special characters cannot exist [ } { # ; \" \' ] ') + return public.return_msg_gettext(False, 'Sent Domain format error :'+get.todomain+'
                                      The following special characters cannot exist [ } { # ; \" \' ] ') if public.get_webserver() != 'openlitespeed' and not get.todomain: get.todomain = "$host" # 检测目标URL格式 if not re.match(rep, get.proxysite): - return public.returnMsg(False, 'DOMAIN_FORMAT', (get.proxysite,)) + return public.return_msg_gettext(False, 'Sent domain format ERROR {}', (get.proxysite,)) if re.search(repte, get.proxysite): - return public.returnMsg(False, "PROXY_URL_ERR", ("?,=,[,],),(,*,&,^,%,$,#,@,!,~,`,{,},>,<,\,',\"]",)) + return public.return_msg_gettext(False, "PROXY_URL_ERR", ("?,=,[,],),(,*,&,^,%,$,#,@,!,~,`,{,},>,<,\,',\"]",)) # 检测目标url是否可用 # if re.match(repd, get.proxysite): # if self.__CheckUrl(get): @@ -3484,9 +3570,9 @@ server for s in subfilter: if not s["sub1"]: if s["sub2"]: - return public.returnMsg(False, 'INPUT_REPLACE_CONTENT') + return public.return_msg_gettext(False, 'Please enter the content to be replaced') elif s["sub1"] == s["sub2"]: - return public.returnMsg(False, 'REP_CON_CANT_SAMEAS_BEREP_CON') + return public.return_msg_gettext(False, 'The content to replace cannot be the same as the content to be replaced') # 设置Nginx配置 def SetNginx(self, get): @@ -3508,9 +3594,11 @@ server self.CheckProxy(get) ng_conf = public.readFile(ng_file) if not p_conf: - rep = "%s[\w\s\~\/\(\)\.\*\{\}\;\$\n\#]+.{1,66}[\s\w\/\*\.\;]+include enable-php-" % public.GetMsg( - "CLEAR_CACHE") - ng_conf = re.sub(rep, 'include enable-php-', ng_conf) + # rep = "%s[\w\s\~\/\(\)\.\*\{\}\;\$\n\#]+.{1,66}[\s\w\/\*\.\;]+include enable-php-" % public.GetMsg( + # "CLEAR_CACHE") + rep = "%s[\w\s\~\/\(\)\.\*\{\}\;\$\n\#]+.*\n.*" % public.get_msg_gettext("#Clear cache") + # ng_conf = re.sub(rep, 'include enable-php-', ng_conf) + ng_conf = re.sub(rep, '', ng_conf) oldconf = '''location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; @@ -3524,7 +3612,7 @@ server access_log off; }''' if "(gif|jpg|jpeg|png|bmp|swf)$" not in ng_conf: - ng_conf = ng_conf.replace('access_log', oldconf + "\n\taccess_log") + ng_conf = re.sub('access_log\s*/www', oldconf + "\n\taccess_log /www",ng_conf) public.writeFile(ng_file, ng_conf) return sitenamelist = [] @@ -3536,15 +3624,17 @@ server if not re.search(rep, ng_conf): rep = "location.+\(gif[\w\|\$\(\)\n\{\}\s\;\/\~\.\*\\\\\?]+access_log\s+/" ng_conf = re.sub(rep, 'access_log /', ng_conf) - ng_conf = ng_conf.replace("include enable-php-", "%s\n" % public.GetMsg( - "CLEAR_CACHE") + cureCache + "\n\t%s\n\t" % public.GetMsg( - "NGINX_PROXY_REP") + "include " + ng_proxyfile + ";\n\n\tinclude enable-php-") + ng_conf = ng_conf.replace("include enable-php-", "%s\n" % public.get_msg_gettext( + "#Clear cache") + cureCache + "\n\t%s\n\t" % public.get_msg_gettext( + "#Referenced reverse proxy rule, if commented, the configured reverse proxy will be invalid") + "include " + ng_proxyfile + ";\n\n\tinclude enable-php-") public.writeFile(ng_file, ng_conf) else: - rep = "%s[\w\s\~\/\(\)\.\*\{\}\;\$\n\#]+.{1,66}[\s\w\/\*\.\;]+include enable-php-" % public.GetMsg( - "CLEAR_CACHE") - ng_conf = re.sub(rep, 'include enable-php-', ng_conf) + # rep = "%s[\w\s\~\/\(\)\.\*\{\}\;\$\n\#]+.{1,66}[\s\w\/\*\.\;]+include enable-php-" % public.GetMsg( + # "CLEAR_CACHE") + rep = "%s[\w\s\~\/\(\)\.\*\{\}\;\$\n\#]+.*\n.*" % public.get_msg_gettext("#Clear cache") + # ng_conf = re.sub(rep, 'include enable-php-', ng_conf) + ng_conf = re.sub(rep,'',ng_conf) oldconf = '''location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; @@ -3558,7 +3648,7 @@ server access_log off; }''' if "(gif|jpg|jpeg|png|bmp|swf)$" not in ng_conf: - ng_conf = ng_conf.replace('access_log', oldconf + "\n\taccess_log") + ng_conf = re.sub('access_log\s*/www', oldconf + "\n\taccess_log /www",ng_conf) public.writeFile(ng_file, ng_conf) # 设置apache配置 @@ -3573,7 +3663,7 @@ server if os.path.exists(ap_file): ap_conf = public.readFile(ap_file) if p_conf == "[]": - rep = "\n*%s\n+\s+IncludeOptiona[\s\w\/\.\*]+" % public.GetMsg("NGINX_PROXY_REP") + rep = "\n*%s\n+\s+IncludeOptiona[\s\w\/\.\*]+" % public.get_msg_gettext('#Referenced reverse proxy rule, if commented, the configured reverse proxy will be invalid') ap_conf = re.sub(rep, '', ap_conf) public.writeFile(ap_file, ap_conf) return @@ -3581,12 +3671,12 @@ server rep = "combined(\n|.)+IncludeOptional.*\/proxy\/.*conf" rep1 = "combined" if not re.search(rep, ap_conf): - ap_conf = ap_conf.replace(rep1, rep1 + "\n\t%s\n\t" % public.GetMsg( - "NGINX_PROXY_REP") + "\n\tIncludeOptional " + ap_proxyfile) + ap_conf = ap_conf.replace(rep1, rep1 + "\n\t%s\n\t" % public.get_msg_gettext( + '#Referenced reverse proxy rule, if commented, the configured reverse proxy will be invalid') + "\n\tIncludeOptional " + ap_proxyfile) public.writeFile(ap_file, ap_conf) else: # rep = "\n*#引用反向代理(\n|.)+IncludeOptional.*\/proxy\/.*conf" - rep = "\n*%s\n+\s+IncludeOptiona[\s\w\/\.\*]+" % public.GetMsg("NGINX_PROXY_REP") + rep = "\n*%s\n+\s+IncludeOptiona[\s\w\/\.\*]+" % public.get_msg_gettext('#Referenced reverse proxy rule, if commented, the configured reverse proxy will be invalid') ap_conf = re.sub(rep, '', ap_conf) public.writeFile(ap_file, ap_conf) @@ -3634,7 +3724,7 @@ RewriteRule ^%s(.*)$ http://%s/$1 [P,E=Proxy-Host:%s] for i in [rewriteconfpath, nginxconfpath, vhostpath]: conf = public.readFile(i) if re.findall(rep, conf): - return public.returnMsg(False, 'NGINX_PROXY_EXIST_ERR') + return public.return_msg_gettext(False, 'A global reverse proxy already exists in the rewrite/nginx master configuration/vhost file') # 创建反向代理 def CreateProxy(self, get): @@ -3650,6 +3740,9 @@ RewriteRule ^%s(.*)$ http://%s/$1 [P,E=Proxy-Host:%s] return self.CheckLocation(get) if not get.proxysite.split('//')[-1]: return public.returnMsg(False, 'The target URL cannot be [http:// or https://], please fill in the full URL, such as: https://aapanel.com') + # project_type = public.M('sites').where('name=?', (get.sitename,)).field('project_type').find()['project_type'] + # if project_type == 'WP': + # return public.return_msg_gettext(False,'Reverse proxies are not currently available for Wordpress sites that use one-click deployment') proxyUrl = self.__read_config(self.__proxyfile) proxyUrl.append({ "proxyname": get.proxyname, @@ -3675,7 +3768,7 @@ RewriteRule ^%s(.*)$ http://%s/$1 [P,E=Proxy-Host:%s] get.siteName = get.sitename self.SetPHPVersion(get) public.serviceReload() - return public.returnMsg(True, 'ADD_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') # 取代理配置文件 def GetProxyFile(self, get): @@ -3688,7 +3781,7 @@ RewriteRule ^%s(.*)$ http://%s/$1 [P,E=Proxy-Host:%s] self.setupPath, get.webserver, sitename, proxyname_md5, sitename) for i in conf: if proxyname == i["proxyname"] and sitename == i["sitename"] and i["type"] != 1: - return public.returnMsg(False, 'PROXY_WAS_STOP') + return public.return_msg_gettext(False, 'Proxy suspended') f = files.files() return f.GetFileBody(get), get.path @@ -3730,13 +3823,15 @@ RewriteRule ^%s(.*)$ http://%s/$1 [P,E=Proxy-Host:%s] for i in range(len(conf)): if conf[i]["proxyname"] == get.proxyname and conf[i]["sitename"] == get.sitename: if int(get.type) != 1: + if not os.path.exists(ng_conf_file): + return public.returnMsg(False,"Please enable the reverse proxy before editing!") public.ExecShell("mv {f} {f}_bak".format(f=ap_conf_file)) public.ExecShell("mv {f} {f}_bak".format(f=ng_conf_file)) public.ExecShell("mv {f} {f}_bak".format(f=ols_conf_file)) conf[i]["type"] = int(get.type) self.__write_config(self.__proxyfile, conf) public.serviceReload() - return public.returnMsg(True, 'EDIT_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') else: if os.path.exists(ap_conf_file + "_bak"): public.ExecShell("mv {f}_bak {f}".format(f=ap_conf_file)) @@ -3749,7 +3844,7 @@ RewriteRule ^%s(.*)$ http://%s/$1 [P,E=Proxy-Host:%s] php_pass_proxy = get.proxysite if get.proxysite[-1] == '/' or get.proxysite.count('/') > 2 or '?' in get.proxysite: php_pass_proxy = re.search('(https?\:\/\/[\w\.]+)', get.proxysite).group(0) - ng_conf = re.sub("location\s+%s" % conf[i]["proxydir"], "location " + get.proxydir, ng_conf) + ng_conf = re.sub("location\s+[\^\~]*\s?%s" % conf[i]["proxydir"], "location ^~ " + get.proxydir, ng_conf) ng_conf = re.sub("proxy_pass\s+%s" % conf[i]["proxysite"], "proxy_pass " + get.proxysite, ng_conf) ng_conf = re.sub("location\s+\~\*\s+\\\.\(php.*\n\{\s*proxy_pass\s+%s.*" % (php_pass_proxy), "location ~* \.(php|jsp|cgi|asp|aspx)$\n{\n\tproxy_pass %s;" % php_pass_proxy,ng_conf) @@ -3894,7 +3989,7 @@ RewriteRule ^%s(.*)$ http://%s/$1 [P,E=Proxy-Host:%s] # os.system("mv %s %s_bak" % (ng_conf_file, ng_conf_file)) if not hasattr(get, 'notreload'): public.serviceReload() - return public.returnMsg(True, 'EDIT_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') # 设置反向代理 @@ -3995,21 +4090,21 @@ location ^~ %s if type == 1 and cache == 1: ng_proxy_cache += ng_proxy % ( proxydir, proxydir, proxysite, get.todomain, - public.GetMsg("NGINX_PERSISTENCE"), ng_sub_filter, ng_cache, get.proxydir) + public.get_msg_gettext('#Persistent connection related configuration'), ng_sub_filter, ng_cache, get.proxydir) if type == 1 and cache == 0: ng_proxy_cache += ng_proxy % ( get.proxydir, get.proxydir, proxysite, get.todomain, - public.GetMsg("NGINX_PERSISTENCE"), ng_sub_filter, no_cache, + public.get_msg_gettext('#Persistent connection related configuration'), ng_sub_filter, 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) + public.get_msg_gettext('#Persistent connection related configuration'), 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, no_cache, + public.get_msg_gettext('#Persistent connection related configuration'), ng_sub_filter, no_cache, get.proxydir) public.writeFile(ng_proxyfile, ng_proxy_cache) @@ -4042,17 +4137,17 @@ location ^~ %s if get.sitename == p_conf[i]["sitename"] and p_conf[i]["proxyname"]: del p_conf[i] self.RemoveProxy(get) - return public.returnMsg(False, 'ERROR: %s
                                      ' % public.GetMsg( - "CONFIG_ERROR") + isError.replace("\n", + return public.return_msg_gettext(False, 'ERROR: %s
                                      ' % public.get_msg_gettext( + 'Configuration ERROR') + isError.replace("\n", '
                                      ') + '
                                      ') - return public.returnMsg(True, 'SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') # 开启缓存 def ProxyCache(self, get): - if public.get_webserver() != 'nginx': return public.returnMsg(False, 'WAF_NOT_NGINX') + if public.get_webserver() != 'nginx': return public.return_msg_gettext(False, 'Currently only support Nginx') file = self.setupPath + "/panel/vhost/nginx/" + get.siteName + ".conf" conf = public.readFile(file) - if conf.find('proxy_pass') == -1: return public.returnMsg(False, 'SET_ERROR') + if conf.find('proxy_pass') == -1: return public.return_msg_gettext(False, 'Failed to set') if conf.find('#proxy_cache') != -1: conf = conf.replace('#proxy_cache', 'proxy_cache') conf = conf.replace('#expires 12h', 'expires 12h') @@ -4062,7 +4157,7 @@ location ^~ %s public.writeFile(file, conf) public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') # 检查反向代理配置 def CheckProxy(self, get): @@ -4107,7 +4202,7 @@ location ^~ %s 'runPath'] rewriteList['rewrite'] = [] - rewriteList['rewrite'].append('0.' + public.getMsg('SITE_REWRITE_NOW')) + rewriteList['rewrite'].append('0.' + public.get_msg_gettext('Current')) for ds in os.listdir('rewrite/' + ws): if ds == 'list.txt': continue rewriteList['rewrite'].append(ds[0:len(ds) - 5]) @@ -4117,12 +4212,14 @@ location ^~ %s # 保存伪静态模板 def SetRewriteTel(self, get): ws = public.get_webserver() + if not get.name: + public.return_msg_gettext(True, 'Please enter a template name') if ws == "openlitespeed": ws = "apache" if sys.version_info[0] == 2: get.name = get.name.encode('utf-8') filename = 'rewrite/' + ws + '/' + get.name + '.conf' public.writeFile(filename, get.data) - return public.returnMsg(True, 'SITE_REWRITE_SAVE') + return public.return_msg_gettext(True, 'New URL rewrite rule has been saved!') # 打包 def ToBackup(self, get): @@ -4138,8 +4235,8 @@ location ^~ %s public.ExecShell(execStr) sql = public.M('backup').add('type,name,pid,filename,size,addtime', (0, fileName, find['id'], zipName, 0, public.getDate())) - public.WriteLog('TYPE_SITE', 'SITE_BACKUP_SUCCESS', (find['name'],)) - return public.returnMsg(True, 'BACKUP_SUCCESS') + public.write_log_gettext('Site manager', 'Backup site [{}] succeed!', (find['name'],)) + return public.return_msg_gettext(True, 'Backup Succeeded!') # 删除备份文件 def DelBackup(self, get): @@ -4153,9 +4250,9 @@ location ^~ %s public.ExecShell( public.get_python_bin() + " " + self.setupPath + '/panel/script/backup_qiniu.py delete_file ' + name) - public.WriteLog('TYPE_SITE', 'SITE_BACKUP_DEL_SUCCESS', (name, filename)) + public.write_log_gettext('Site manager', 'Successfully deleted backup [{}] of site [{}]!', (name, filename)) public.M('backup').where(where, (id,)).delete() - return public.returnMsg(True, 'DEL_SUCCESS') + return public.return_msg_gettext(True, 'Successfully deleted') # 旧版本配置文件处理 def OldConfigFile(self): @@ -4280,7 +4377,7 @@ location ^~ %s def toSiteDatabase(self, name, domains, path): if public.M('sites').where('name=?', (name,)).count() > 0: return public.M('sites').add('name,path,status,ps,addtime', - (name, path, '1', public.GetMsg("INPUT_DB_PS"), public.getDate())) + (name, path, '1', public.get_msg_gettext('Please enter a note'), public.getDate())) pid = public.M('sites').where("name=?", (name,)).getField('id') for domain in domains: public.M('domain').add('pid,name,port,addtime', (pid, domain, '80', public.getDate())) @@ -4349,7 +4446,7 @@ location ^~ %s public.writeFile(filename,conf) public.serviceReload() - return public.returnMsg(True, 'SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') # 取日志状态 def GetLogsStatus(self, get): @@ -4380,16 +4477,19 @@ location ^~ %s # 设置目录加密 def SetHasPwd(self, get): if public.get_webserver() == 'openlitespeed': - return public.returnMsg(False, 'NOT_SUPPORT_OLS') - if len(get.username.strip()) == 0 or len(get.password.strip()) == 0: return public.returnMsg(False, - 'LOGIN_USER_EMPTY') + return public.return_msg_gettext(False, 'The current web server is openlitespeed. This function is not supported yet.') + if len(get.username.strip()) < 3 or len(get.password.strip()) < 3: return public.return_msg_gettext(False, + 'Username or password cannot be less than 3 digits!') if not hasattr(get, 'siteName'): get.siteName = public.M('sites').where('id=?', (get.id,)).getField('name') self.CloseHasPwd(get) filename = public.GetConfigValue('setup_path') + '/pass/' + get.siteName + '.pass' - passconf = get.username + ':' + public.hasPwd(get.password) + try: + passconf = get.username + ':' + public.hasPwd(get.password) + except: + return public.returnMsg(False,"The password fomart is wrong, please do not use special symbols for the first two digits!") if get.siteName == 'phpmyadmin': get.configFile = self.setupPath + '/nginx/conf/nginx.conf' @@ -4439,8 +4539,8 @@ location ^~ %s if not os.path.exists(passDir): public.ExecShell('mkdir -p ' + passDir) public.writeFile(filename, passconf) public.serviceReload() - public.WriteLog("TYPE_SITE", "SITE_AUTH_OPEN_SUCCESS", (get.siteName,)) - return public.returnMsg(True, 'SET_SUCCESS') + public.write_log_gettext("Site manager", "Set site [{}] to password authentication required!", (get.siteName,)) + return public.return_msg_gettext(True, 'Setup successfully!') # 取消目录加密 def CloseHasPwd(self, get): @@ -4470,8 +4570,8 @@ location ^~ %s conf = conf.replace(' #Require all granted', " Require all granted") public.writeFile(get.configFile, conf) public.serviceReload() - public.WriteLog("TYPE_SITE", "SITE_AUTH_CLOSE_SUCCESS", (get.siteName,)) - return public.returnMsg(True, 'SET_SUCCESS') + public.write_log_gettext("Site manager", "Cleared password authentication for site [{}]!", (get.siteName,)) + return public.return_msg_gettext(True, 'Setup successfully!') # 启用tomcat支持 def SetTomcat(self, get): @@ -4479,7 +4579,7 @@ location ^~ %s name = siteName.replace('.', '_') rep = "^(\d{1,3}\.){3,3}\d{1,3}$" - if re.match(rep, siteName): return public.returnMsg(False, 'TOMCAT_IP') + if re.match(rep, siteName): return public.return_msg_gettext(False, 'ERROR, primary domain cannot be IP address!') # nginx filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' @@ -4536,8 +4636,8 @@ location ^~ %s public.ExecShell('/etc/init.d/tomcat stop') public.ExecShell('/etc/init.d/tomcat start') public.ExecShell('echo "127.0.0.1 ' + siteName + '" >> /etc/hosts') - public.WriteLog('TYPE_SITE', 'SITE_TOMCAT_OPEN', (siteName,)) - return public.returnMsg(True, 'SITE_TOMCAT_OPEN') + public.write_log_gettext('TYPE_SITE', 'Turned on Tomcat supporting for site [{}]!', (siteName,)) + return public.return_msg_gettext(True, 'Succeeded, please test JSP program!') # 关闭tomcat支持 def CloseTomcat(self, get): @@ -4569,14 +4669,14 @@ location ^~ %s public.serviceReload() public.ExecShell('/etc/init.d/tomcat restart') public.ExecShell("sed -i '/" + siteName + "/d' /etc/hosts") - public.WriteLog('TYPE_SITE', 'SITE_TOMCAT_CLOSE', (siteName,)) - return public.returnMsg(True, 'SITE_TOMCAT_CLOSE') + public.write_log_gettext('Site manager', 'Turned off Tomcat supporting for site [{}]!', (siteName,)) + return public.return_msg_gettext(True, 'Tomcat mapping closed!') - # 取当站点前运行目录 - def GetSiteRunPath(self, get): - siteName = public.M('sites').where('id=?', (get.id,)).getField('name') - sitePath = public.M('sites').where('id=?', (get.id,)).getField('path') - if not siteName: return {"runPath":"/",'dirs':[]} + #取当站点前运行目录 + def GetSiteRunPath(self,get): + siteName = public.M('sites').where('id=?',(get.id,)).getField('name') + sitePath = public.M('sites').where('id=?',(get.id,)).getField('path') + if not siteName or os.path.isfile(sitePath): return {"runPath":"/",'dirs':[]} path = sitePath if public.get_webserver() == 'nginx': filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' @@ -4585,7 +4685,7 @@ location ^~ %s rep = '\s*root\s+(.+);' path = re.search(rep, conf) if not path: - return public.returnMsg(False, "GET_RUN_PATH_FAILED") + return public.return_msg_gettext(False, 'Get Site run path false') path = path.groups()[0] elif public.get_webserver() == 'apache': filename = self.setupPath + '/panel/vhost/apache/' + siteName + '.conf' @@ -4594,7 +4694,7 @@ location ^~ %s rep = '\s*DocumentRoot\s*"(.+)"\s*\n' path = re.search(rep, conf) if not path: - return public.returnMsg(False, "GET_RUN_PATH_FAILED") + return public.return_msg_gettext(False, 'Get Site run path false') path = path.groups()[0] else: filename = self.setupPath + '/panel/vhost/openlitespeed/' + siteName + '.conf' @@ -4603,7 +4703,7 @@ location ^~ %s rep = "vhRoot\s*(.*)" path = re.search(rep, conf) if not path: - return public.returnMsg(False, "GET_RUN_PATH_FAILED") + return public.return_msg_gettext(False, 'Get Site run path false') path = path.groups()[0] data = {} if sitePath == path: @@ -4641,19 +4741,25 @@ location ^~ %s filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' if os.path.exists(filename): conf = public.readFile(filename) - rep = '\s*root\s+(.+);' - path = re.search(rep, conf).groups()[0] - conf = conf.replace(path, sitePath + get.runPath) - public.writeFile(filename, conf) + if conf: + rep = '\s*root\s+(.+);' + tmp = re.search(rep,conf) + if tmp: + path = tmp.groups()[0] + conf = conf.replace(path,sitePath + get.runPath) + public.writeFile(filename,conf) - # 处理Apache + #处理Apache filename = self.setupPath + '/panel/vhost/apache/' + siteName + '.conf' if os.path.exists(filename): conf = public.readFile(filename) - rep = '\s*DocumentRoot\s*"(.+)"\s*\n' - path = re.search(rep, conf).groups()[0] - conf = conf.replace(path, sitePath + get.runPath) - public.writeFile(filename, conf) + if conf: + rep = '\s*DocumentRoot\s*"(.+)"\s*\n' + tmp = re.search(rep,conf) + if tmp: + path = tmp.groups()[0] + conf = conf.replace(path,sitePath + get.runPath) + public.writeFile(filename,conf) # 处理OLS self._set_ols_run_path(sitePath, get.runPath, siteName) # self.DelUserInI(sitePath) @@ -4667,7 +4773,7 @@ location ^~ %s public.ExecShell("chattr +i {}".format(d_path)) public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') def _set_ols_run_path(self, site_path, run_path, sitename): ols_conf_file = "{}/panel/vhost/openlitespeed/{}.conf".format(self.setupPath, sitename) @@ -4716,7 +4822,7 @@ location ^~ %s if get.name == '0': if os.path.exists(default_site_save): os.remove(default_site_save) public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') # 处理新的 path = self.setupPath + '/apache/htdocs' @@ -4747,12 +4853,12 @@ location ^~ %s if os.path.exists(path): public.ExecShell('rm -f ' + path) public.writeFile(default_site_save, get.name) public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') # 取默认站点 def GetDefaultSite(self, get): data = {} - data['sites'] = public.M('sites').field('name').order('id desc').select() + data['sites'] = public.M('sites').where('project_type=? OR project_type=?',('PHP','WP')).field('name').order('id desc').select() data['defaultSite'] = public.readFile('data/defaultSite.pl') return data @@ -4768,11 +4874,11 @@ location ^~ %s 'setup_path') + "/panel/class && " + public.get_python_bin() + " panelSafe.pyc " + get.path sql = db.Sql() sql.table('tasks').add('id,name,type,status,addtime,execstr', ( - None, '%s [' % public.GetMsg("SCAN_DIR") + get.path + ']', 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), + None, '%s [' % public.get_msg_gettext('Scan directory') + get.path + ']', 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr)) public.writeFile(isTask, 'True') - public.WriteLog('TYPE_SETUP', 'SITE_SCAN_ADD', (get.path,)) - return public.returnMsg(True, 'SITE_SCAN_ADD') + public.write_log_gettext('Installer', 'Added trojan scan task for directory [{}]!', (get.path,)) + return public.return_msg_gettext(True, 'Scan Task has in the queue!') # 获取结果信息 def GetCheckSafe(self, get): @@ -4794,10 +4900,10 @@ location ^~ %s conf = public.httpGet(public.getUrl() + '/install/ruleList.conf') if conf: public.writeFile(self.setupPath + '/panel/data/ruleList.conf', conf) - return public.returnMsg(True, 'UPDATE_SUCCESS') - return public.returnMsg(False, 'CONNECT_ERR') + return public.return_msg_gettext(True, 'Update Succeeded!') + return public.return_msg_gettext(False, 'Failed to connect server!') except: - return public.returnMsg(False, 'CONNECT_ERR') + return public.return_msg_gettext(False, 'Failed to connect server!') def set_site_etime_multiple(self, get): ''' @@ -4818,9 +4924,9 @@ location ^~ %s self.SetEdate(get) set_edate_successfully.append(site_name) except: - set_edate_failed[site_name] = 'SET_ERROR1' + set_edate_failed[site_name] = 'There was an error setting, please try again.' pass - return {'status': True, 'msg': public.getMsg('SET_EDATE_MULTIPLE', (','.join(set_edate_successfully),)), + return {'status': True, 'msg': public.get_msg_gettext('Set the website [{}] expiration time successfully', (','.join(set_edate_successfully),)), 'error': set_edate_failed, 'success': set_edate_successfully} @@ -4828,15 +4934,15 @@ location ^~ %s def SetEdate(self, get): result = public.M('sites').where('id=?', (get.id,)).setField('edate', get.edate) siteName = public.M('sites').where('id=?', (get.id,)).getField('name') - public.WriteLog('TYPE_SITE', 'SITE_EXPIRE_SUCCESS', (siteName, get.edate)) - return public.returnMsg(True, 'SITE_EXPIRE_SUCCESS') + public.write_log_gettext('Site manager', 'Set expired date to [{}] for site[{}]!', (get.edate,siteName)) + return public.return_msg_gettext(True, 'Successfully set, the site will stop automatically when expires!') # 获取防盗链状态 def GetSecurity(self, get): file = '/www/server/panel/vhost/nginx/' + get.name + '.conf' conf = public.readFile(file) data = {} - if type(conf) == bool: return public.returnMsg(False, 'CONF_FILE_NOT_EXISTS') + if type(conf) == bool: return public.return_msg_gettext(False, 'Configuration file not exist') if conf.find('SECURITY-START') != -1: rep = "#SECURITY-START(\n|.)+#SECURITY-END" tmp = re.search(rep, conf).group() @@ -4868,25 +4974,32 @@ 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_EMPTY_ERR') + if len(get.fix) < 2: return public.return_msg_gettext(False, 'URL suffix cannot be empty!') + if len(get.domains) < 3: return public.return_msg_gettext(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) if get.status == '1': + if conf.find('SECURITY-START') == -1: return public.return_msg_gettext(False,'Please turn on the hotlink first!') r_key = 'valid_referers none blocked' d_key = 'valid_referers' if conf.find(r_key) == -1: conf = conf.replace(d_key, r_key) else: - if conf.find('SECURITY-START') == -1: return public.returnMsg(False, 'ANTI_THEFT_ERR') conf = conf.replace(r_key, d_key) else: if conf.find('SECURITY-START') != -1: rep = "\s{0,4}#SECURITY-START(\n|.){1,500}#SECURITY-END\n?" conf = re.sub(rep, '', conf) - public.WriteLog('TYPE_SITE', "SITE_STOP_ANTI_STEALING_LINK", (get.name,)) + public.write_log_gettext('Site manager', "Hotlink Protection for site [{}] disabled!", (get.name,)) + # 先替换域名部分,防止域名过多导致替换失败 + rep = "\s+valid_referers.+" + conf = re.sub(rep,'',conf) + # 再替换配置部分 + rep = "\s+#SECURITY-START(\n|.){1,500}#SECURITY-END\n?" + conf = re.sub(rep,'',conf) + public.write_log_gettext('Site manager', "Hotlink Protection for site [{}] disabled!", (get.name,)) else: return_rule = 'return 404' if 'return_rule' in get: @@ -4895,7 +5008,7 @@ location ^~ %s return_rule = 'return {}'.format(get.return_rule) else: if get.return_rule[0] != '/': - return public.returnMsg(False, "RESPONSE_ERR") + return public.return_msg_gettext(False, 'Response resources should use URI path or HTTP status code, such as: /test.png or 404') return_rule = 'rewrite /.* {} break'.format(get.return_rule) rconf = '''%s location ~ .*\.(%s)$ @@ -4908,10 +5021,10 @@ location ^~ %s } } #SECURITY-END - include enable-php-''' % (public.GetMsg("SECURITY_START"), get.fix.strip().replace(',', '|'), + include enable-php-''' % (public.get_msg_gettext('#SECURITY-START Hotlink protection configuration'), get.fix.strip().replace(',', '|'), get.domains.strip().replace(',', ' '), return_rule) conf = re.sub("include\s+enable-php-", rconf, conf) - public.WriteLog('TYPE_SITE', "SITE_START_ANTI_STEALING_LINK", (get.name,)) + public.write_log_gettext('Site manager', "Hotlink Protection for site [{}] enabled!", (get.name,)) public.writeFile(file, conf) file = '/www/server/panel/vhost/apache/' + get.name + '.conf' @@ -4923,7 +5036,7 @@ location ^~ %s if conf.find(r_key) == -1: conf = conf.replace(d_key, r_key) else: - if conf.find('SECURITY-START') == -1: return public.returnMsg(False, 'ANTI_THEFT_ERR') + if conf.find('SECURITY-START') == -1: return public.return_msg_gettext(False, 'Please turn on anti-theft first!') conf = conf.replace(r_key, d_key) else: if conf.find('SECURITY-START') != -1: @@ -4937,7 +5050,7 @@ location ^~ %s return_rule = '/{s}.html [R={s},NC,L]'.format(s=get.return_rule) else: if get.return_rule[0] != '/': - return public.returnMsg(False, "RESPONSE_ERR") + return public.return_msg_gettext(False, 'Response resources should use URI path or HTTP status code, such as: /test.png or 404') return_rule = '{}'.format(get.return_rule) tmp = " RewriteCond %{HTTP_REFERER} !{DOMAIN} [NC]" @@ -4945,8 +5058,8 @@ location ^~ %s for d in get.domains.split(','): tmps.append(tmp.replace('{DOMAIN}', d)) domains = "\n".join(tmps) - rconf = "combined\n " + public.GetMsg( - "SECURITY_START") + "\n RewriteEngine on\n" + domains + "\n RewriteRule .(" + get.fix.strip().replace( + rconf = "combined\n " + public.get_msg_gettext( + '#SECURITY-START Hotlink protection configuration') + "\n RewriteEngine on\n" + domains + "\n RewriteRule .(" + get.fix.strip().replace( ',', '|') + ") " + return_rule + "\n #SECURITY-END" conf = conf.replace('combined', rconf) public.writeFile(file, conf) @@ -4974,7 +5087,19 @@ RewriteRule \.(BTPFILE)$ /404.html [R,NC] if get.status == "false": public.ExecShell('rm -f {}'.format(file)) public.serviceReload() - return public.returnMsg(True, 'SET_SUCCESS') + return public.return_msg_gettext(True, 'Setup successfully!') + + # xss 防御 + def xsssec(self,text): + replace_list = { + "<":"<", + ">":">", + "'":"'", + '"':""", + } + for k,v in replace_list.items(): + text = text.replace(k,v) + return public.xssencode2(text) # 取网站日志 def GetSiteLogs(self, get): @@ -4985,8 +5110,8 @@ RewriteRule \.(BTPFILE)$ /404.html [R,NC] logPath = '/www/wwwlogs/' + get.siteName + '-access_log' else: logPath = '/www/wwwlogs/' + get.siteName + '_ols.access_log' - if not os.path.exists(logPath): return public.returnMsg(False, 'LOG_EMPTY') - return public.returnMsg(True, public.GetNumLines(logPath, 1000)) + if not os.path.exists(logPath): return public.return_msg_gettext(False, 'Log is empty') + return public.return_msg_gettext(True, self.xsssec(public.GetNumLines(logPath, 1000))) # 取网站日志 def get_site_err_log(self, get): @@ -4997,43 +5122,43 @@ RewriteRule \.(BTPFILE)$ /404.html [R,NC] logPath = '/www/wwwlogs/' + get.siteName + '-error_log' else: logPath = '/www/wwwlogs/' + get.siteName + '_ols.error_log' - if not os.path.exists(logPath): return public.returnMsg(False, 'LOG_EMPTY') - return public.returnMsg(True, public.GetNumLines(logPath, 1000)) + if not os.path.exists(logPath): return public.return_msg_gettext(False, 'Log is empty') + return public.return_msg_gettext(True, self.xsssec(public.GetNumLines(logPath, 1000))) # 取网站分类 def get_site_types(self, get): data = public.M("site_types").field("id,name").order("id asc").select() - data.insert(0, {"id": 0, "name": public.GetMsg("DEFAULT_SORT")}) + data.insert(0, {"id": 0, "name": public.get_msg_gettext('Default category')}) return data # 添加网站分类 def add_site_type(self, get): get.name = get.name.strip() - if not get.name: return public.returnMsg(False, "SORT_NAME_ERR") - if len(get.name) > 18: return public.returnMsg(False, "SORT_NAME_LEN_ERR") + if not get.name: return public.return_msg_gettext(False, 'Category name cannot be empty') + if len(get.name) > 18: return public.return_msg_gettext(False, 'Category name cannot exceed 6 Chinese characters or 18 letters') type_sql = public.M('site_types') - if type_sql.count() >= 10: return public.returnMsg(False, 'SORT_MOST') - if type_sql.where('name=?', (get.name,)).count() > 0: return public.returnMsg(False, "SORT_NAME_EXIST") - type_sql.add("name", (get.name,)) - return public.returnMsg(True, 'ADD_SUCCESS') + if type_sql.count() >= 10: return public.return_msg_gettext(False, 'Add up to 10 categories!') + if type_sql.where('name=?', (get.name,)).count() > 0: return public.return_msg_gettext(False, 'Specified category name already exists!') + type_sql.add("name",(public.xssencode2(get.name),)) + return public.return_msg_gettext(True, 'Setup successfully!') # 删除网站分类 def remove_site_type(self, get): type_sql = public.M('site_types') - if type_sql.where('id=?', (get.id,)).count() == 0: return public.returnMsg(False, "SORT_NOT_EXIST") + if type_sql.where('id=?', (get.id,)).count() == 0: return public.return_msg_gettext(False, 'Specified category does NOT exist!') type_sql.where('id=?', (get.id,)).delete() public.M("sites").where("type_id=?", (get.id,)).save("type_id", (0,)) - return public.returnMsg(True, "SORT_DEL") + return public.return_msg_gettext(True, 'Category deleted!') # 修改网站分类名称 def modify_site_type_name(self, get): get.name = get.name.strip() - if not get.name: return public.returnMsg(False, "SORT_NAME_ERR") - if len(get.name) > 18: return public.returnMsg(False, "SORT_NAME_LEN_ERR") + if not get.name: return public.return_msg_gettext(False, 'Category name cannot be empty') + if len(get.name) > 18: return public.return_msg_gettext(False, 'Category name cannot exceed 6 Chinese characters or 18 letters') type_sql = public.M('site_types') - if type_sql.where('id=?', (get.id,)).count() == 0: return public.returnMsg(False, "SORT_NOT_EXIST") + if type_sql.where('id=?', (get.id,)).count() == 0: return public.return_msg_gettext(False, 'Specified category does NOT exist!') type_sql.where('id=?', (get.id,)).setField('name', get.name) - return public.returnMsg(True, "EDIT_SUCCESS") + return public.return_msg_gettext(True, 'Successfully modified') # 设置指定站点的分类 def set_site_type(self, get): @@ -5041,7 +5166,7 @@ RewriteRule \.(BTPFILE)$ /404.html [R,NC] site_sql = public.M("sites") for s_id in site_ids: site_sql.where("id=?", (s_id,)).setField("type_id", get.id) - return public.returnMsg(True, "SET_SUCCESS") + return public.returnMsg(True, "Setup successfully!") # 设置目录保护 def set_dir_auth(self, get): @@ -5069,9 +5194,9 @@ RewriteRule \.(BTPFILE)$ /404.html [R,NC] continue del_successfully.append(name) except: - del_failed[name] = public.getMsg('DEL_ERROR1') + del_failed[name] = public.get_msg_gettext('There was an error deleting, please try again.') public.serviceReload() - return {'status': True, 'msg': public.getMsg('DEL_DIR_AUTH_MULTIPLE', (','.join(del_successfully),)), + return {'status': True, 'msg': public.get_msg_gettext('Delete [ {} ] dir auth successfully', (','.join(del_successfully),)), 'error': del_failed, 'success': del_successfully} @@ -5133,8 +5258,7 @@ RewriteRule \.(BTPFILE)$ /404.html [R,NC] result = [] import database - db_data = database.database().get_database_size(None) - + db_data = database.database().get_database_size(ids,True) limit_size = 50 * 1024 * 1024 f_list_size = [];db_list_size = [] for id in ids: @@ -5167,7 +5291,7 @@ RewriteRule \.(BTPFILE)$ /404.html [R,NC] data['database'] = db_data[find['name']] data['database']['st_time'] = db_addtime - db_score = 0; + db_score = 0 db_size = data['database']['total'] if db_size > 0: @@ -5180,4 +5304,272 @@ RewriteRule \.(BTPFILE)$ /404.html [R,NC] slist['data'] = sorted(result,key= lambda x:x['score'],reverse=True) slist['file_size'] = self.get_average_num(f_list_size) slist['db_size'] = self.get_average_num(db_list_size) - return slist \ No newline at end of file + return slist + + def get_https_mode(self, get=None): + ''' + @name 获取https模式 + @author hwliang<2022-01-14> + @return bool False.宽松模式 True.严格模式 + ''' + web_server = public.get_webserver() + if web_server not in ['nginx', 'apache']: + return False + + if web_server == 'nginx': + default_conf_file = "{}/nginx/0.default.conf".format(public.get_vhost_path()) + else: + default_conf_file = "{}/apache/0.default.conf".format(public.get_vhost_path()) + + if not os.path.exists(default_conf_file): return False + default_conf = public.readFile(default_conf_file) + if not default_conf: return False + + if default_conf.find('DEFAULT SSL CONFI') != -1: return True + return False + + def write_ngx_default_conf_by_ssl(self): + ''' + @name 写nginx默认配置文件(含SSL配置) + @author hwliang<2022-01-14> + @return bool + ''' + default_conf_body = '''server +{ + listen 80; + listen 443 ssl; + server_name _; + index index.html; + root /www/server/nginx/html; + + # DEFAULT SSL CONFIG + ssl_certificate /www/server/panel/vhost/cert/0.default/fullchain.pem; + ssl_certificate_key /www/server/panel/vhost/cert/0.default/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + add_header Strict-Transport-Security "max-age=31536000"; +}''' + ngx_default_conf_file = "{}/nginx/0.default.conf".format(public.get_vhost_path()) + self.create_default_cert() + return public.writeFile(ngx_default_conf_file, default_conf_body) + + def write_ngx_default_conf(self): + ''' + @name 写nginx默认配置文件 + @author hwliang<2022-01-14> + @return bool + ''' + default_conf_body = '''server +{ + listen 80; + server_name _; + index index.html; + root /www/server/nginx/html; +}''' + ngx_default_conf_file = "{}/nginx/0.default.conf".format(public.get_vhost_path()) + return public.writeFile(ngx_default_conf_file, default_conf_body) + + def write_apa_default_conf_by_ssl(self): + ''' + @name 写nginx默认配置文件(含SSL配置) + @author hwliang<2022-01-14> + @return bool + ''' + default_conf_body = ''' + ServerAdmin webmaster@example.com + DocumentRoot "/www/server/apache/htdocs" + ServerName bt.default.com + + SetOutputFilter DEFLATE + Options FollowSymLinks + AllowOverride All + Order allow,deny + Allow from all + DirectoryIndex index.html + + + + ServerAdmin webmaster@example.com + DocumentRoot "/www/server/apache/htdocs" + ServerName ssl.default.com + + # DEFAULT SSL CONFIG + SSLEngine On + SSLCertificateFile /www/server/panel/vhost/cert/0.default/fullchain.pem + SSLCertificateKeyFile /www/server/panel/vhost/cert/0.default/privkey.pem + SSLCipherSuite EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5 + SSLProtocol All -SSLv2 -SSLv3 -TLSv1 + SSLHonorCipherOrder On + + + SetOutputFilter DEFLATE + Options FollowSymLinks + AllowOverride All + Order allow,deny + Allow from all + DirectoryIndex index.html + +''' + apa_default_conf_file = "{}/apache/0.default.conf".format(public.get_vhost_path()) + self.create_default_cert() + return public.writeFile(apa_default_conf_file, default_conf_body) + + def write_apa_default_conf(self): + ''' + @name 写apache默认配置文件 + @author hwliang<2022-01-14> + @return bool + ''' + default_conf_body = ''' + ServerAdmin webmaster@example.com + DocumentRoot "/www/server/apache/htdocs" + ServerName bt.default.com + + SetOutputFilter DEFLATE + Options FollowSymLinks + AllowOverride All + Order allow,deny + Allow from all + DirectoryIndex index.html + +''' + apa_default_conf_file = "{}/apache/0.default.conf".format(public.get_vhost_path()) + return public.writeFile(apa_default_conf_file, default_conf_body) + + def set_https_mode(self, get=None): + ''' + @name 设置https模式 + @author hwliang<2022-01-14> + @return dict + ''' + web_server = public.get_webserver() + if web_server not in ['nginx', 'apache']: + return public.return_msg_gettext(False, 'This function only supports Nginx/Apache') + + ngx_default_conf_file = "{}/nginx/0.default.conf".format(public.get_vhost_path()) + apa_default_conf_file = "{}/apache/0.default.conf".format(public.get_vhost_path()) + ngx_default_conf = public.readFile(ngx_default_conf_file) + apa_default_conf = public.readFile(apa_default_conf_file) + status = False + if ngx_default_conf: + if ngx_default_conf.find('DEFAULT SSL CONFIG') != -1: + status = False + self.write_ngx_default_conf() + self.write_apa_default_conf() + else: + status = True + self.write_ngx_default_conf_by_ssl() + self.write_apa_default_conf_by_ssl() + else: + status = True + self.write_ngx_default_conf_by_ssl() + self.write_apa_default_conf_by_ssl() + + public.serviceReload() + status_msg = {True: 'Open', False: 'Close'} + msg = public.gettext_msg('Has {} HTTPS strict mode',(status_msg[status],)) + public.write_log_gettext('WebSite manager', msg) + return public.return_msg_gettext(True, msg) + + def create_default_cert(self): + ''' + @name 创建默认SSL证书 + @author hwliang<2022-01-14> + @return bool + ''' + cert_pem = '/www/server/panel/vhost/cert/0.default/fullchain.pem' + cert_key = '/www/server/panel/vhost/cert/0.default/privkey.pem' + if os.path.exists(cert_pem) and os.path.exists(cert_key): return True + cert_path = os.path.dirname(cert_pem) + if not os.path.exists(cert_path): os.makedirs(cert_path) + import OpenSSL + key = OpenSSL.crypto.PKey() + key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) + cert = OpenSSL.crypto.X509() + cert.set_serial_number(0) + # cert.get_subject().CN = '' + cert.set_issuer(cert.get_subject()) + cert.gmtime_adj_notBefore(0) + cert.gmtime_adj_notAfter(86400 * 3650) + cert.set_pubkey(key) + cert.sign(key, 'md5') + cert_ca = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) + private_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) + if len(cert_ca) > 100 and len(private_key) > 100: + public.writeFile(cert_pem, cert_ca, 'wb+') + public.writeFile(cert_key, private_key, 'wb+') + return True + return False + + def auto_restart_rph(self,get): + #设置申请或续签SSL时自动停止反向代理、重定向、http to https,申请完成后自动开启 + conf_file = '{}/data/stop_rp_when_renew_ssl.pl'.format(public.get_panel_path()) + conf = public.readFile(conf_file) + if not conf: + public.writeFile(conf_file,json.dumps([get.sitename])) + try: + conf = json.loads(conf) + if get.sitename not in conf: + conf.append(get.sitename) + public.writeFile(conf_file,json.dumps(conf)) + except: + return public.returnMsg(True,'Error parsing configuration file') + return public.returnMsg(True,'Setup successfully') + + def remove_auto_restart_rph(self,get): + #设置申请或续签SSL时自动停止反向代理、重定向、http to https,申请完成后自动开启 + conf_file = '{}/data/stop_rp_when_renew_ssl.pl'.format(public.get_panel_path()) + conf = public.readFile(conf_file) + if not conf: + return public.returnMsg(False,'Website [proxy,redirect,http to https] are not set to restart automatically') + try: + conf = json.loads(conf) + conf.remove(get.sitename) + public.writeFile(conf_file,json.dumps(conf)) + except: + return public.returnMsg(False,'Configuration file parsing error') + return public.returnMsg(True,'Setup successfully') + + def get_auto_restart_rph(self,get): + #设置申请或续签SSL时自动停止反向代理、重定向、http to https,申请完成后自动开启 + conf_file = '{}/data/stop_rp_when_renew_ssl.pl'.format(public.get_panel_path()) + conf = public.readFile(conf_file) + if not conf: + return public.returnMsg(False,'Website [proxy,redirect,http to https] are not set to restart automatically') + try: + conf = json.loads(conf) + if get.sitename in conf: + return public.returnMsg(True, 'This website has turn on [proxy,redirect,http to https] auto restart') + return public.returnMsg(False, 'Website has turn off auto restart') + except: + return public.returnMsg(False,'Configuration file parsing error') + + def reset_wp_password(self,get): + return one_key_wp.one_key_wp().reset_wp_password(get) + + def is_update (self,get): + return one_key_wp.one_key_wp().is_update(get) + + def purge_all_cache(self,get): + return one_key_wp.one_key_wp().purge_all_cache(get) + + def set_fastcgi_cache(self,get): + return one_key_wp.one_key_wp().set_fastcgi_cache(get) + + def update_wp(self,get): + return one_key_wp.one_key_wp().update_wp(get) + + def get_language(self,get): + return one_key_wp.one_key_wp().get_language(get) + + def deploy_wp(self,get): + return one_key_wp.one_key_wp().deploy_wp(get) + + def get_wp_username(self,get): + return one_key_wp.one_key_wp().get_wp_username(get) + + def reset_wp_db(self,get): + return one_key_wp.one_key_wp().reset_wp_db(get) \ No newline at end of file diff --git a/class/panelTask.py b/class/panelTask.py index f2332ef2..f7ef0fad 100644 --- a/class/panelTask.py +++ b/class/panelTask.py @@ -140,7 +140,7 @@ class bt_task: "kill -9 $(ps aux|grep '"+task_info['shell']+"'|grep -v grep|awk '{print $2}')") public.ExecShell("/etc/init.d/bt start") - return public.returnMsg(True, 'TASK_CANCEL') + return public.return_msg_gettext(True, 'Task cancelled!') # 取一条任务 def get_task_find(self, id): @@ -187,7 +187,6 @@ class bt_task: self.backup_site(task_shell, log_file) elif task_type == 7: # 恢复网站 pass - # 标记状态与结束时间 self.modify_task(id, 'status', 1) self.modify_task(id, 'endtime', int(time.time())) @@ -235,7 +234,7 @@ class bt_task: if not os.path.exists(log_file): data = '' if(task_type == '1'): - data = {'name': public.GetMsg("DOWNLOAD_FILE"), 'total': 0, 'used': 0, + data = {'name': public.get_msg_gettext('Download file'), 'total': 0, 'used': 0, 'pre': 0, 'speed': 0, 'time': 0} return data @@ -262,7 +261,7 @@ class bt_task: speed_total = re.findall( r"([\d\.]+[BbKkMmGg]).+\s+(\d+)%\s+([\d\.]+[KMBGkmbg])\s+(\w+[sS])", speed_tmp) if not speed_total: - data = {'name':public.getMsg('DOWNLOAD_FILE1',(filename,)),'total':0,'used':0,'pre':0,'speed':0,'time':0} + data = {'name':public.get_msg_gettext('Download file {}',(filename,)),'total':0,'used':0,'pre':0,'speed':0,'time':0} else: speed_total = speed_total[0] used = speed_total[0] @@ -271,11 +270,13 @@ class bt_task: float(speed_total[0].lower().replace('k', '')) * 1024) u_time = speed_total[3].replace( 'h', 'Hour').replace('m', 'Minute').replace('s', 'Second') - data = {'name': public.getMsg('DOWNLOAD_FILE1',(filename,)),'total': total, 'used': used, 'pre': speed_total[1], 'speed': speed_total[2], 'time': u_time} + data = {'name': public.get_msg_gettext('Download file {}',(filename,)),'total': total, 'used': used, 'pre': speed_total[1], 'speed': speed_total[2], 'time': u_time} else: data = public.ExecShell("tail -n {} {}".format(num, log_file))[0] if type(data) == list: return '' + if isinstance(data,bytes): + data = data.decode('utf-8') data = data.replace('\x08', '').replace('\n', '
                                      ') return data @@ -303,7 +304,7 @@ class bt_task: path = path.encode('utf-8') if sfile.find(',') == -1: if not os.path.exists(path+'/'+sfile): - return public.returnMsg(False, 'FILE_NOT_EXISTS') + return public.return_msg_gettext(False, 'Configuration file not exist') # 处理多文件压缩 sfiles = '' for sfile in sfile.split(','): @@ -325,11 +326,11 @@ class bt_task: public.ExecShell("cd '" + path + "' && "+rar_file + " a -r '" + dfile + "' " + sfiles + " &> " + log_file) else: - return public.returnMsg(False,'NOT_SUP_COMP_FORMAT') + return public.return_msg_gettext(False,'Specified compression format is not supported!') self.set_file_accept(dfile) - #public.WriteLog("TYPE_FILE", 'ZIP_SUCCESS', (sfiles, dfile),not_web = self.not_web) - return public.returnMsg(True, 'ZIP_SUCCESS') + #public.WriteLog("TYPE_FILE", 'Compression succeeded!', (sfiles, dfile),not_web = self.not_web) + return public.return_msg_gettext(True, 'Compression succeeded!') # 文件解压 def _unzip(self, sfile, dfile, password, log_file): @@ -337,7 +338,7 @@ class bt_task: sfile = sfile.encode('utf-8') dfile = dfile.encode('utf-8') if not os.path.exists(sfile): - return public.returnMsg(False, 'FILE_NOT_EXISTS') + return public.return_msg_gettext(False, 'Configuration file not exist') # 判断压缩包格式 if sfile[-4:] == '.zip': @@ -372,8 +373,8 @@ class bt_task: user = pwd.getpwuid(os.stat(dfile).st_uid).pw_name public.ExecShell("chown %s:%s %s" % (user, user, dfile)) - #public.WriteLog("TYPE_FILE", 'UNZIP_SUCCESS', (sfile, dfile),not_web = self.not_web) - return public.returnMsg(True, 'UNZIP_SUCCESS') + #public.WriteLog("TYPE_FILE", 'Uncompression succeeded!', (sfile, dfile),not_web = self.not_web) + return public.return_msg_gettext(True, 'Uncompression succeeded!') # 备份网站 def backup_site(self, id, log_file): @@ -395,7 +396,7 @@ class bt_task: sql = public.M('backup').add('type,name,pid,filename,size,addtime', (0, fileName, find['id'], zipName, 0, public.getDate())) public.WriteLog('TYPE_SITE', 'SITE_BACKUP_SUCCESS', (find['name'],),not_web = self.not_web) - return public.returnMsg(True, 'BACKUP_SUCCESS') + return public.return_msg_gettext(True, 'Backup Succeeded!') # 备份数据库 def backup_database(self, id, log_file): @@ -413,7 +414,7 @@ class bt_task: public.ExecShell("/www/server/mysql/bin/mysqldump --force --opt \"" + name + "\" | gzip > " + backupName) if not os.path.exists(backupName): - return public.returnMsg(False, 'BACKUP_ERROR') + return public.return_msg_gettext(False, 'Backup error!') self.mypass(False, find['mysql_root']) @@ -422,7 +423,7 @@ class bt_task: sql.add('type,name,pid,filename,size,addtime', (1, fileName, id, backupName, 0, addTime)) public.WriteLog("TYPE_DATABASE", "DATABASE_BACKUP_SUCCESS", (name,),not_web = self.not_web) - return public.returnMsg(True, 'BACKUP_SUCCESS') + return public.return_msg_gettext(True, 'Backup Succeeded!') # 导入数据库 def input_database(self, id, file, log_file): @@ -432,7 +433,7 @@ class bt_task: exts = ['sql', 'gz', 'zip'] ext = tmp[len(tmp) - 1] if ext not in exts: - return public.returnMsg(False, 'DATABASE_INPUT_ERR_FORMAT') + return public.return_msg_gettext(False, 'Select sql/gz/zip file!') isgzip = False if ext != 'sql': @@ -454,7 +455,7 @@ class bt_task: isgzip = True if not os.path.exists(backupPath + '/' + tmpFile) or tmpFile == '': - return public.returnMsg(False, 'FILE_NOT_EXISTS', (tmpFile,)) + return public.return_msg_gettext(False, 'Configuration file not exist', (tmpFile,)) self.mypass(True, root) public.ExecShell(public.GetConfigValue('setup_path') + "/mysql/bin/mysql -uroot -p" + root + " --force \"" + name + "\" < " + backupPath + '/' + tmpFile) @@ -470,8 +471,8 @@ class bt_task: 'setup_path') + "/mysql/bin/mysql -uroot -p" + root + " --force \"" + name + "\" < " + file) self.mypass(False, root) - public.WriteLog("TYPE_DATABASE", 'DATABASE_INPUT_SUCCESS', (name,),not_web = self.not_web) - return public.returnMsg(True, 'DATABASE_INPUT_SUCCESS') + public.WriteLog("TYPE_DATABASE", 'Successfully imported database [{}]', (name,),not_web = self.not_web) + return public.return_msg_gettext(True, 'Successfully imported database!') # 配置 def mypass(self, act, root): diff --git a/class/panelVideo.py b/class/panelVideo.py index 9f302b76..d6cf5694 100644 --- a/class/panelVideo.py +++ b/class/panelVideo.py @@ -18,14 +18,8 @@ from BTPanel import Response def get_buff_size(file_size): buff_size = 2097152 - if file_size < 104857600: - buff_size = 2097152 - elif file_size < 524288000: + if file_size > 1073741824: buff_size = 4194304 - elif file_size < 1073741824: - buff_size = 8388608 - else: - buff_size = 10485760 return buff_size def partial_response(path, start, end=None): diff --git a/class/panelWaf.py b/class/panelWaf.py index 0e215e3b..b76b3f71 100644 --- a/class/panelWaf.py +++ b/class/panelWaf.py @@ -1,6 +1,6 @@ #!/usr/bin/python #coding: utf-8 -# Author: lkq@bt.cn +# Author: lkqiang # panelWaf.py # code: 面板基础安全类 # +------------------------------------------------------------------- diff --git a/class/panelWarning.py b/class/panelWarning.py index cd8b05c9..dfb82729 100644 --- a/class/panelWarning.py +++ b/class/panelWarning.py @@ -48,21 +48,25 @@ class panelWarning: } result_file = self.__result + '/' + m_name + '.pl' - not_force = True - if 'force' in args: - not_force = m_info['ignore'] + # not_force = True + # if 'force' in args: + # not_force = m_info['ignore'] - if os.path.exists(result_file) and not_force: - m_info['status'],m_info['msg'],m_info['check_time'],m_info['taking'] = json.loads(public.readFile(result_file)) - else: - try: - s_time = time.time() - m_info['status'],m_info['msg'] = p[m_name].check_run() - m_info['taking'] = round(time.time() - s_time,6) - m_info['check_time'] = int(time.time()) - public.writeFile(result_file,json.dumps([m_info['status'],m_info['msg'],m_info['check_time'],m_info['taking']],)) - except: - continue + # if os.path.exists(result_file) and not_force: + # try: + # m_info['status'],m_info['msg'],m_info['check_time'],m_info['taking'] = json.loads(public.readFile(result_file)) + # except: + # if os.path.exists(result_file): os.remove(result_file) + # continue + # else: + try: + s_time = time.time() + m_info['status'],m_info['msg'] = p[m_name].check_run() + m_info['taking'] = round(time.time() - s_time,6) + m_info['check_time'] = int(time.time()) + public.writeFile(result_file,json.dumps([m_info['status'],m_info['msg'],m_info['check_time'],m_info['taking']],)) + except: + continue if m_info['ignore']: diff --git a/class/panel_restore.py b/class/panel_restore.py index 5dd84383..485b3f3b 100644 --- a/class/panel_restore.py +++ b/class/panel_restore.py @@ -60,7 +60,7 @@ class panel_restore: # 判断备份文件是否存在,如果不存在继续检查是否远程备份 if not os.path.exists(local_backup_file_path): self._progress_rewrite('No backup file found: {}'.format(str(local_backup_file_path))) - return public.returnMsg(False, 'Panel does not find the backup file: {}'.format(local_backup_file_path)) + return public.return_msg_gettext(False, 'Panel does not find the backup file: {}'.format(local_backup_file_path)) # 将网站目录移至回收站 self._progress_rewrite('Move the current website directory to the recycle bin: {}'.format(str(args.path))) self._remove_old_website_file_to_trush(args) @@ -158,14 +158,14 @@ class panel_restore: self._download_google_drive_file(args) result = self._restore_backup(self._local_file, site_info, args) else: - return public.ExecShell(False,'Currently only supports restoring local, Google storage and AWS S3 backups') + return public.return_msg_gettext(False,'Currently only supports restoring local, Google storage and AWS S3 backups') if os.path.exists(self._local_file): os.remove(self._local_file) if result: self._progress_rewrite('Recovery failed: {}'.format(str(site_info['site_path']))) return result self._progress_rewrite('Successful recovery: {}'.format(str(site_info['site_path']))) - return public.returnMsg(True,'Restore Successful') + return public.return_msg_gettext(True,'Restore Successful') # 取任务进度 def get_progress(self, get): @@ -187,6 +187,8 @@ class panel_restore: @parma file_name 备份得文件名 /www/backup/database/db_test_com_20200817_112722.sql.gz|Google Drive|db_test_com_20200817_112722.sql.gz @parma obj_name 数据库名 """ + if "|" not in args.file: + return public.returnMsg(True,'success') try: backup_info = args.file.split('|') args.file_name = backup_info[-1] @@ -202,7 +204,8 @@ class panel_restore: elif backup_method == 'Google Drive': self._download_google_drive_file(args) else: - return public.ExecShell(False,'Currently only supports restoring local, Google storage and AWS S3 backups') + return public.returnMsg(False,'Currently only supports restoring local, Google storage and AWS S3 backups') public.ExecShell('mv {} {}/database'.format(self._local_file, self._get_local_backup_path())) + return public.returnMsg(True,'success') except: - return False + return public.returnMsg(False,"Download error!") diff --git a/class/panel_telegram_bot.py b/class/panel_telegram_bot.py index adb2052d..2ed38ebb 100644 --- a/class/panel_telegram_bot.py +++ b/class/panel_telegram_bot.py @@ -17,7 +17,7 @@ pip = public.get_pip_bin() try: import telegram except: - public.ExecShell('{} install telegram'.format(pip)) + public.ExecShell('{} install python-telegram-bot'.format(pip)) import telegram class panel_telegram_bot: diff --git a/class/plugin_deployment.py b/class/plugin_deployment.py index 432cbf70..03820027 100644 --- a/class/plugin_deployment.py +++ b/class/plugin_deployment.py @@ -12,7 +12,7 @@ #+-------------------------------------------------------------------- import public,json,os,time,sys,re -from BTPanel import session +from BTPanel import session,cache class obj: id=0 class plugin_deployment: __setupPath = 'data' @@ -21,6 +21,7 @@ class plugin_deployment: __tmp = '/www/server/panel/temp/' timeoutCount = 0 oldTime = 0 + _speed_key = 'dep_download_speed' #获取列表 def GetList(self,get): @@ -76,7 +77,7 @@ class plugin_deployment: if sys.version_info[0] == 2: filename = filename.encode('utf-8') if os.path.exists(filename): if os.path.getsize(filename) > 100: return pinfo - public.ExecShell("wget -O " + filename + ' http://www.bt.cn' + m_uri + " &") + public.ExecShell("wget -O " + filename + ' https://www.bt.cn' + m_uri + " &") return pinfo #获取插件列表 @@ -103,7 +104,7 @@ class plugin_deployment: try: jsonFile = self.__setupPath + '/deployment_list.json' if not 'package' in session or not os.path.exists(jsonFile) or hasattr(get,'force'): - downloadUrl = 'http://www.bt.cn/api/panel/get_deplist' + downloadUrl = 'https://www.bt.cn/api/panel/get_deplist' pdata = public.get_pdata() tmp = json.loads(public.httpPost(downloadUrl,pdata,3)) if not tmp: return public.returnMsg(False,'Failed to get from the cloud!') @@ -269,7 +270,7 @@ class plugin_deployment: #下载文件 if isDownload: self.WriteLogs(json.dumps({'name':'Downloading file ...','total':0,'used':0,'pre':0,'speed':0})) - if pinfo['versions'][0]['download']: self.DownloadFile('http://www.bt.cn/api/Pluginother/get_file?fname=' + pinfo['versions'][0]['download'], packageZip) + if pinfo['versions'][0]['download']: self.DownloadFile('https://www.bt.cn/api/Pluginother/get_file?fname=' + pinfo['versions'][0]['download'], packageZip) if not os.path.exists(packageZip): return public.returnMsg(False,'File download failed!' + packageZip) @@ -470,7 +471,7 @@ class plugin_deployment: p = panelAuth.panelAuth() pdata = p.create_serverid(None); pdata['pid'] = id; - p_url = 'http://www.bt.cn/api/pluginother/create_order_okey' + p_url = 'https://www.bt.cn/api/pluginother/create_order_okey' public.httpPost(p_url,pdata) #获取进度 diff --git a/class/projectModel/base.py b/class/projectModel/base.py index 59c8de8b..c47f27cb 100644 --- a/class/projectModel/base.py +++ b/class/projectModel/base.py @@ -1,2 +1,63 @@ +#coding: utf-8 +import public,re + class projectBase: - pass \ No newline at end of file + + def check_port(self, port): + ''' + @name 检查端口是否被占用 + @args port:端口号 + @return: 被占用返回True,否则返回False + @author: lkq 2021-08-28 + ''' + a = public.ExecShell("netstat -nltp|awk '{print $4}'") + if a[0]: + if re.search(':' + port + '\n', a[0]): + return True + else: + return False + else: + return False + + def is_domain(self, domain): + ''' + @name 验证域名合法性 + @args domain:域名 + @return: 合法返回True,否则返回False + @author: lkq 2021-08-28 + ''' + import re + domain_regex = re.compile(r'(?:[A-Z0-9_](?:[A-Z0-9-_]{0,247}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(? @return list ''' - plugin_name = None + project_name = None for pid_name in os.listdir(self._node_pid_path): pid_file = '{}/{}'.format(self._node_pid_path,pid_name) - s_pid = int(public.readFile(pid_file)) + #s_pid = int(public.readFile(pid_file)) + data = public.readFile(pid_file) + if isinstance(data,str) and data: + s_pid = int(data) + else: + return [] if pid == s_pid: - plugin_name = pid_name[:-4] + project_name = pid_name[:-4] break - project_find = self.get_project_find(plugin_name) + project_find = self.get_project_find(project_name) if not project_find: return [] if not self._pids: self._pids = psutil.pids() all_pids = [] for i in self._pids: try: p = psutil.Process(i) - if p.cwd() == project_find['path'] and p.username() == project_find['project_config']['run_user']: - if p.name() in ['node','npm','pm2']: + if p.cwd() == project_find['path']: + pname = p.name() + if pname in ['node','npm','pm2','yarn'] or pname.find('node ') == 0: + cmdline = ','.join(p.cmdline()) + if cmdline.find('God Daemon') != -1:continue + env_list = p.environ() + if 'name' in env_list: + if not env_list['name'] == project_name: continue + if 'NODE_PROJECT_NAME' in env_list: + if not env_list['NODE_PROJECT_NAME'] == project_name: continue all_pids.append(i) except: continue return all_pids + def get_project_state_by_cwd(self,project_name): + ''' + @name 通过cwd获取项目状态 + @author hwliang<2022-01-17> + @param project_name 项目名称 + @return bool or list + ''' + project_find = self.get_project_find(project_name) + self._pids = psutil.pids() + if not project_find: return [] + all_pids = [] + for i in self._pids: + try: + p = psutil.Process(i) + if p.cwd() == project_find['path']: + pname = p.name() + if pname in ['node','npm','pm2','yarn'] or pname.find('node ') == 0: + cmdline = ','.join(p.cmdline()) + if cmdline.find('God Daemon') != -1:continue + env_list = p.environ() + if 'name' in env_list: + if not env_list['name'] == project_name: continue + if 'NODE_PROJECT_NAME' in env_list: + if not env_list['NODE_PROJECT_NAME'] == project_name: continue + all_pids.append(i) + except: continue + if all_pids: + pid_file = "{}/{}.pid".format(self._node_pid_path,project_name) + public.writeFile(pid_file,str(all_pids[0])) + return all_pids + return False + def kill_pids(self,get=None,pids = None): ''' @name 结束进程列表 @@ -1175,7 +1221,10 @@ export PATH nodejs_version = project_find['project_config']['nodejs_version'] node_bin = self.get_node_bin(nodejs_version) npm_bin = self.get_npm_bin(nodejs_version) - project_script = project_find['project_config']['project_script'].strip() + project_script = project_find['project_config']['project_script'].strip().replace(' ',' ') + if project_script[:3] == 'pm2': # PM2启动方式处理 + project_script = project_script.replace('pm2 ','pm2 -u {} -n {} '.format(project_find['project_config']['run_user'],get.project_name)) + project_find['project_config']['run_user'] = 'root' log_file = "{}/{}.log".format(self._node_logs_path,get.project_name) if not project_script: return public.return_error('No startup script configured') @@ -1184,6 +1233,7 @@ export PATH # 生成启动脚本 if os.path.exists(project_script): start_cmd = '''{last_env} +export NODE_PROJECT_NAME="{project_name}" cd {project_cwd} nohup {node_bin} {project_script} 2>&1 >> {log_file} & echo $! > {pid_file} @@ -1193,10 +1243,12 @@ echo $! > {pid_file} project_script = project_script, log_file = log_file, pid_file = pid_file, - last_env = last_env + last_env = last_env, + project_name = get.project_name ) elif project_script in scripts_keys: start_cmd = '''{last_env} +export NODE_PROJECT_NAME="{project_name}" cd {project_cwd} nohup {npm_bin} run {project_script} 2>&1 >> {log_file} & echo $! > {pid_file} @@ -1206,10 +1258,12 @@ echo $! > {pid_file} project_script = project_script, pid_file = pid_file, log_file = log_file, - last_env = last_env + last_env = last_env, + project_name = get.project_name ) else: start_cmd = '''{last_env} +export NODE_PROJECT_NAME="{project_name}" cd {project_cwd} nohup {project_script} 2>&1 >> {log_file} & echo $! > {pid_file} @@ -1218,7 +1272,8 @@ echo $! > {pid_file} project_script = project_script, pid_file = pid_file, log_file = log_file, - last_env = last_env + last_env = last_env, + project_name = get.project_name ) script_file = "{}/{}.sh".format(self._node_run_scripts,get.project_name) @@ -1236,6 +1291,10 @@ echo $! > {pid_file} # 执行脚本文件 p = public.ExecShell("bash {}".format(script_file),user=project_find['project_config']['run_user']) time.sleep(1) + n = 0 + while n < 5: + if self.get_project_state_by_cwd(get.project_name): break + n+=1 if not os.path.exists(pid_file): p = '\n'.join(p) if p.find('[Errno 0]') != -1: @@ -1245,7 +1304,10 @@ echo $! > {pid_file} return public.return_error('failed to activate
                                      {}
                                      '.format(p)) # 获取PID - pid = int(public.readFile(pid_file)) + try: + pid = int(public.readFile(pid_file)) + except: + return public.return_error('Startup failed
                                      {}'.format(public.GetNumLines(log_file,20))) pids = self.get_project_pids(pid=pid) if not pids: if os.path.exists(pid_file): os.remove(pid_file) @@ -1263,13 +1325,33 @@ echo $! > {pid_file} } @return dict ''' + project_find = self.get_project_find(get.project_name) + if not project_find: return public.return_error('Project does not exist') + project_script = project_find['project_config']['project_script'].strip().replace(' ',' ') pid_file = "{}/{}.pid".format(self._node_pid_path,get.project_name) - if not os.path.exists(pid_file): return public.return_error('Project did not start') - pid = int(public.readFile(pid_file)) - pids = self.get_project_pids(pid=pid) - if not pids: return public.return_error('Project did not start') - self.kill_pids(pids=pids) + if project_script.find('pm2 start') != -1: # 处理PM2启动的项目 + nodejs_version = project_find['project_config']['nodejs_version'] + last_env = self.get_last_env(nodejs_version,project_find['path']) + project_script = project_script.replace('pm2 start','pm2 stop') + public.ExecShell('''{} +cd {} +{}'''.format(last_env,project_find['path'],project_script)) + else: + pid_file = "{}/{}.pid".format(self._node_pid_path,get.project_name) + if not os.path.exists(pid_file): return public.return_error('Project did not start') + data = public.readFile(pid_file) + if isinstance(data,str) and data: + pid = int(data) + pids = self.get_project_pids(pid=pid) + else: + return public.return_error('Project did not start') + if not pids: return public.return_error('Project did not start') + self.kill_pids(pids=pids) if os.path.exists(pid_file): os.remove(pid_file) + time.sleep(0.5) + pids = self.get_project_state_by_cwd(get.project_name) + if pids: self.kill_pids(pids=pids) + return public.return_data(True, 'Stopped successfully') def restart_project(self,get): @@ -1314,8 +1396,12 @@ echo $! > {pid_file} load_info = {} pid_file = "{}/{}.pid".format(self._node_pid_path,project_name) if not os.path.exists(pid_file): return load_info - pid = int(public.readFile(pid_file)) - pids = self.get_project_pids(pid=pid) + data = public.readFile(pid_file) + if isinstance(data,str) and data: + pid = int(data) + pids = self.get_project_pids(pid=pid) + else: + return load_info if not pids: return load_info for i in pids: process_info = self.get_process_info_by_pid(i) @@ -1514,9 +1600,13 @@ echo $! > {pid_file} if get: project_name = get.project_name.strip() pid_file = "{}/{}.pid".format(self._node_pid_path,project_name) if not os.path.exists(pid_file): return False - pid = int(public.readFile(pid_file)) - pids = self.get_project_pids(pid=pid) - if not pids: return False + data=public.readFile(pid_file) + if isinstance(data,str) and data: + pid = int(data) + pids = self.get_project_pids(pid=pid) + else: + return self.get_project_state_by_cwd(project_name) + if not pids: return self.get_project_state_by_cwd(project_name) return True def get_project_find(self,project_name): @@ -1556,12 +1646,16 @@ echo $! > {pid_file} ''' project_info['project_config'] = json.loads(project_info['project_config']) project_info['run'] = self.get_project_run_state(project_name = project_info['name']) - project_info['load_info'] = self.get_project_load_info(project_name = project_info['name']) + project_info['load_info'] = {} + if project_info['run']: + project_info['load_info'] = self.get_project_load_info(project_name = project_info['name']) project_info['ssl'] = self.get_ssl_end_date(project_name = project_info['name']) project_info['listen'] = [] project_info['listen_ok'] = True if project_info['load_info']: for pid in project_info['load_info'].keys(): + if not 'connections' in project_info['load_info'][pid]: + project_info['load_info'][pid]['connections'] = [] for conn in project_info['load_info'][pid]['connections']: if not conn['status'] == 'LISTEN': continue if not conn['local_port'] in project_info['listen']: diff --git a/class/public.py b/class/public.py index 516eaef4..e6ada91c 100644 --- a/class/public.py +++ b/class/public.py @@ -12,6 +12,10 @@ # -------------------------------- import json,os,sys,time,re,socket,importlib,binascii,base64,io,string +import gettext +es = gettext.translation('en', localedir='/www/server/panel/BTPanel/static/language', languages=['en']) +es.install() +_ = es.gettext from random import choice _LAN_PUBLIC = None _LAN_LOG = None @@ -45,9 +49,11 @@ def HttpGet(url,timeout = 6,headers = {}): @timeout 超时时间默认60秒 @return string """ - if is_local(): return False + if url.find('GetAuthToken') == -1: + if is_local(): return False + # rep_home_host() import http_requests - res = http_requests.get(url,timeout=timeout,headers = headers,verify=False) + res = http_requests.get(url,timeout=timeout,headers = headers) if res.status_code == 0: if headers: return False s_body = res.text @@ -108,7 +114,9 @@ def HttpPost(url,data,timeout = 6,headers = {}): @timeout 超时时间默认60秒 return string """ - if is_local(): return False + if url.find('GetAuthToken') == -1: + if is_local(): return False + # rep_home_host() import http_requests res = http_requests.post(url,data=data,timeout=timeout,headers = headers) if res.status_code == 0: @@ -192,7 +200,8 @@ def ReturnJson(status,msg,args=()): @param msg 返回消息 @return string(json) """ - return GetJson(ReturnMsg(status, msg, args)) + # return GetJson(ReturnMsg(status, msg, args)) + return GetJson(return_msg_gettext(status,msg,args)) def returnJson(status,msg,args=()): """ @@ -225,6 +234,10 @@ def ReturnMsg(status,msg,args = ()): msg = msg.replace(rep,args[i]) return {'status':status,'msg':msg} +def return_msg_gettext(status,msg,args = ()): + msg = gettext_msg(msg,args) + return {'status':status,'msg':msg} + def returnMsg(status,msg,args = ()): """ @name 取通用dict返回 @@ -269,7 +282,10 @@ def GetJson(data): from json import dumps if data == bytes: data = data.decode('utf-8') try: - return dumps(data,ensure_ascii=False) + try: + return dumps(data) + except: + return dumps(data,ensure_ascii=False) except: return dumps(returnMsg(False,"WRONG_RESPONSE", (str(data)))) @@ -284,22 +300,23 @@ def ReadFile(filename,mode = 'r'): """ import os if not os.path.exists(filename): return False + fp = None try: fp = open(filename, mode) f_body = fp.read() - fp.close() except Exception as ex: if sys.version_info[0] != 2: try: - fp = open(filename, mode,encoding="utf-8") + fp = open(filename, mode,encoding="utf-8",errors='ignore') f_body = fp.read() - fp.close() except: - fp = open(filename, mode,encoding="GBK") + fp = open(filename, mode,encoding="GBK",errors='ignore') f_body = fp.read() - fp.close() else: return False + finally: + if fp and not fp.closed: + fp.close() return f_body def readFile(filename,mode='r'): @@ -344,6 +361,38 @@ def writeFile(filename,s_body,mode='w+'): ''' return WriteFile(filename,s_body,mode) +def gettext_msg(msg,args=()): + try: + msg = _(msg).format(*args) + except: + pass + finally: + return msg + +def write_log_gettext(type, logmsg, args=(), not_web = False): + #写日志 + logmsg = gettext_msg(logmsg,args) + try: + import time,db,json + username = 'system' + uid = 1 + tmp_msg = '' + if not not_web: + try: + from BTPanel import session + if 'username' in session: + username = session['username'] + uid = session['uid'] + if session.get('debug') == 1: return + except: + pass + sql = db.Sql() + mDate = time.strftime('%Y-%m-%d %X',time.localtime()) + data = (uid,username,_(type),xssencode2(logmsg + tmp_msg),mDate) + result = sql.table('logs').add('uid,username,type,log,addtime',data) + except: + pass + def WriteLog(type,logMsg,args=(),not_web = False): #写日志 try: @@ -372,10 +421,11 @@ def WriteLog(type,logMsg,args=(),not_web = False): if type in keys: type = _LAN_LOG[type] sql = db.Sql() mDate = time.strftime('%Y-%m-%d %X',time.localtime()) - data = (uid,username,type,xssencode2(logMsg + tmp_msg),mDate) + data = (uid,username,type,logMsg + tmp_msg,mDate) result = sql.table('logs').add('uid,username,type,log,addtime',data) + return result except: - pass + return None def GetLanguage(): ''' @@ -446,6 +496,10 @@ def GetMsg(key,args = ()): return msg except: return key + +def get_msg_gettext(msg,args = ()): + return gettext_msg(msg,args) + def getMsg(key,args = ()): return GetMsg(key,args) @@ -527,7 +581,7 @@ def ExecShell(cmdstring, timeout=None, shell=True,cwd=None,env=None,user = None) if timeout: s = 0 d = 0.01 - while sub.poll() is None: + while sub.poll() == None: time.sleep(d) s += d if s >= timeout: @@ -549,7 +603,9 @@ def ExecShell(cmdstring, timeout=None, shell=True,cwd=None,env=None,user = None) #编码修正 if type(a) == bytes: a = a.decode('utf-8') if type(e) == bytes: e = e.decode('utf-8') - except:pass + except: + a = str(a) + e = str(e) return a,e @@ -559,24 +615,37 @@ def GetLocalIp(): filename = 'data/iplist.txt' ipaddress = readFile(filename) if not ipaddress: - url = 'http://pv.sohu.com/cityjson?ie=utf-8' + url = 'https://ifconfig.me/ip' m_str = HttpGet(url) if isinstance(m_str,bytes): - ipaddress = re.search('\d+.\d+.\d+.\d+', m_str.decode('utf-8')).group(0) + ipaddress = re.search(r"^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$", m_str.decode('utf-8')).group(0) else: - ipaddress = re.search('\d+.\d+.\d+.\d+', m_str).group(0) + ipaddress = re.search(r"^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$", m_str).group(0) WriteFile(filename,ipaddress) c_ip = check_ip(ipaddress) if not c_ip: return GetHost() return ipaddress - except: + except Exception as e: try: - url = 'https://brandnew.aapanel.com/api/common/getClientIP' - return HttpGet(url) + url = 'https://www.aapanel.com/api/common/getClientIP' + ipaddress = HttpGet(url) + WriteFile(filename, ipaddress) + return ipaddress except: return GetHost() def is_ipv4(ip): + ''' + @name 是否是IPV4地址 + @author hwliang + @param ip IP地址 + @return True/False + ''' + #验证基本格式 + if not re.match(r"^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$", ip): + return False + + # 验证每个段是否在合理范围 try: socket.inet_pton(socket.AF_INET, ip) except AttributeError: @@ -584,13 +653,23 @@ def is_ipv4(ip): socket.inet_aton(ip) except socket.error: return False - return ip.count('.') == 3 except socket.error: return False return True def is_ipv6(ip): + ''' + @name 是否为IPv6地址 + @author hwliang + @param ip 地址 + @return True/False + ''' + # 验证基本格式 + if not re.match(r"^[\w:]+$", ip): + return False + + # 验证IPv6地址 try: socket.inet_pton(socket.AF_INET6, ip) except socket.error: @@ -604,33 +683,32 @@ def check_ip(ip): def GetHost(port = False): from flask import request host_tmp = request.headers.get('host') + + # 验证基本格式 + if host_tmp: + if not re.match("^[\w\.\:\-]+$", host_tmp): + host_tmp = '' + if not host_tmp: if request.url_root: - tmp = re.findall(r"(https|http)://([\[\]\w:\.-]+)",request.url_root) + tmp = re.findall(r"(https|http)://([\w:\.-]+)",request.url_root) if tmp: host_tmp = tmp[0][1] if not host_tmp: - host_tmp = GetLocalIp() + ':' + readFile('data/port.pl').strip() + host_tmp = '127.0.0.1:' + readFile('data/port.pl').strip() try: if host_tmp.find(':') == -1: host_tmp += ':80' except: host_tmp = "127.0.0.1:7800" h = host_tmp.split(':') if port: return h[-1] - if len(h) > 2: - h = ":".join(h[:-1]) - return h - return h[0] - - - + return ':'.join(h[0:-1]) def GetClientIp(): from flask import request - ipaddr = request.remote_addr.replace('::ffff:','') - if not check_ip(ipaddr): return '未知IP地址' + ipaddr = request.remote_addr.replace('::ffff:','') + if not check_ip(ipaddr): return 'Unknown IP address' return ipaddr - def get_client_ip(): return GetClientIp() @@ -723,14 +801,17 @@ def checkInput(data): #取文件指定尾行数 def GetNumLines(path,num,p=1): + if not os.path.exists(path): return "" + if isinstance(num,str) and not re.match("\d+",num): + return "" + pyVersion = sys.version_info[0] - max_len = 1024*128 + max_len = 1024*1024*10 try: - import cgi - if not os.path.exists(path): return "" + start_line = (p - 1) * num count = start_line + num - fp = open(path,'r') + fp = open(path,'rb') buf = "" fp.seek(-1, 2) if fp.read(1) == "\n": fp.seek(-1, 2) @@ -751,7 +832,7 @@ def GetNumLines(path,num,p=1): if sp_len > 0: line = line[sp_len:] try: - data.insert(0,cgi.escape(line)) + data.insert(0,line) except: pass buf = buf[:newline_pos] n += 1 @@ -764,9 +845,8 @@ def GetNumLines(path,num,p=1): fp.seek(-to_read, 1) t_buf = fp.read(to_read) if pyVersion == 3: - try: - if type(t_buf) == bytes: t_buf = t_buf.decode('utf-8') - except:t_buf = str(t_buf) + t_buf = t_buf.decode('utf-8',errors='ignore') + buf = t_buf + buf fp.seek(-to_read, 1) if pos - to_read == 0: @@ -775,8 +855,8 @@ def GetNumLines(path,num,p=1): if not b: break fp.close() result = "\n".join(data) - if not result: raise Exception('null') except: + if re.match("[`\$\&\;]+",path): return "" result = ExecShell("tail -n {} {}".format(num,path))[0] if len(result) > max_len: result = result[-max_len:] @@ -795,19 +875,23 @@ def GetNumLines(path,num,p=1): #验证证书 def CheckCert(certPath = 'ssl/certificate.pem'): - openssl = '/usr/local/openssl/bin/openssl' - if not os.path.exists(openssl): openssl = 'openssl' - certPem = readFile(certPath) - s = "\n-----BEGIN CERTIFICATE-----" - tmp = certPem.strip().split(s) - for tmp1 in tmp: - if tmp1.find('-----BEGIN CERTIFICATE-----') == -1: tmp1 = s + tmp1 - writeFile(certPath, tmp1) - result = ExecShell(openssl + " x509 -in " + certPath + " -noout -subject") - if result[1].find('-bash:') != -1: return True - if len(result[1]) > 2: return False - if result[0].find('error:') != -1: return False - return True + try: + return get_cert_data(certPath) + except: + openssl = '/usr/local/openssl/bin/openssl' + if not os.path.exists(openssl): openssl = 'openssl' + certPem = readFile(certPath) + s = "\n-----BEGIN CERTIFICATE-----" + tmp = certPem.strip().split(s) + res = True + for tmp1 in tmp: + if tmp1.find('-----BEGIN CERTIFICATE-----') == -1: tmp1 = s + tmp1 + writeFile(certPath,tmp1) + result = ExecShell(openssl + " x509 -in "+certPath+" -noout -subject") + if result[1].find('-bash:') != -1: res = True + if len(result[1]) > 2: res = False + if result[0].find('error:') != -1: res = False + return res # 获取面板地址 @@ -817,7 +901,7 @@ def getPanelAddr(): return protocol + request.headers.get('host') -# 字节单位转换 +#字节单位转换 def to_size(size): if not size: return '0.00 b' size = float(size) @@ -830,9 +914,9 @@ def to_size(size): return ("%.2f" % size) + ' ' + b -def checkCode(code, outime=120): - # 校验验证码 - from BTPanel import session, cache +def checkCode(code,outime = 120): + #校验验证码 + from BTPanel import session,cache try: codeStr = cache.get('codeStr') cache.delete('codeStr') @@ -970,9 +1054,9 @@ REQUEST_FORM: {request_form} request_date = getDate(), remote_addr = GetClientIp(), method = request.method, - full_path = request.full_path, + full_path = url_encode(xsssec(request.full_path)), request_form = request.form.to_dict(), - user_agent = request.headers.get('User-Agent'), + user_agent = xsssec(request.headers.get('User-Agent')), panel_version = version(), os_version = get_os_version() ) @@ -1118,26 +1202,26 @@ def CheckMyCnf(): PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH -CF='node.aapanel.com' -HK='download.bt.cn' -HK2='103.224.251.67' -US='128.1.164.196' -sleep 0.5; -CN_PING=`ping -c 1 -w 1 $CF|grep time=|awk '{print $7}'|sed "s/time=//"` -HK_PING=`ping -c 1 -w 1 $HK|grep time=|awk '{print $7}'|sed "s/time=//"` -HK2_PING=`ping -c 1 -w 1 $HK2|grep time=|awk '{print $7}'|sed "s/time=//"` -US_PING=`ping -c 1 -w 1 $US|grep time=|awk '{print $7}'|sed "s/time=//"` +# CF='node.aapanel.com' +# HK='download.bt.cn' +# HK2='103.224.251.67' +# US='128.1.164.196' +# sleep 0.5; +# CN_PING=`ping -c 1 -w 1 $CF|grep time=|awk '{print $7}'|sed "s/time=//"` +# HK_PING=`ping -c 1 -w 1 $HK|grep time=|awk '{print $7}'|sed "s/time=//"` +# HK2_PING=`ping -c 1 -w 1 $HK2|grep time=|awk '{print $7}'|sed "s/time=//"` +# US_PING=`ping -c 1 -w 1 $US|grep time=|awk '{print $7}'|sed "s/time=//"` +# +# echo "$HK_PING $HK" > ping.pl +# echo "$HK2_PING $HK2" >> ping.pl +# echo "$US_PING $US" >> ping.pl +# echo "$CF_PING $CF" >> ping.pl +# nodeAddr=`sort -V ping.pl|sed -n '1p'|awk '{print $2}'` +# if [ "$nodeAddr" == "" ];then +# nodeAddr=$CF +# fi -echo "$HK_PING $HK" > ping.pl -echo "$HK2_PING $HK2" >> ping.pl -echo "$US_PING $US" >> ping.pl -echo "$CF_PING $CF" >> ping.pl -nodeAddr=`sort -V ping.pl|sed -n '1p'|awk '{print $2}'` -if [ "$nodeAddr" == "" ];then - nodeAddr=$CF -fi - -Download_Url=http://$nodeAddr:5880 +Download_Url=https://node.aapanel.com MySQL_Opt() @@ -1238,22 +1322,100 @@ def GetSSHPort(): except: return 22 +def get_sshd_port(): + ''' + @name 获取sshd端口 + @author hwliang + @return int + ''' + # 先尝试从进程中获取当前实际的监听端口 + sshd_port = 22 + is_ok = 0 + pid = get_sshd_pid_of_pidfile() + if not pid: pid = get_sshd_pid_of_binfile() + if pid: + try: + import psutil + p = psutil.Process(pid) + for conn in p.connections(): + if conn.status == 'LISTEN': + sshd_port = conn.laddr[1] + is_ok = 1 + break + except:pass + + # 如果从进程获取失败,则尝试从配置文件获取 + if not is_ok: sshd_port = GetSSHPort() + + return sshd_port + + + +def get_sshd_pid_of_pidfile(): + ''' + @name 通过PID文件获取SSH状态 + @author hwliang + @return int 0:关闭 pid:开启 + ''' + sshd_pid_list =['/run/sshd.pid','/var/run/sshd.pid','/run/ssh.pid','/var/run/ssh.pid'] + sshd_pid_file = None + for spid_file in sshd_pid_list: + if os.path.exists(spid_file): + sshd_pid_file = spid_file + break + + if sshd_pid_file: + sshd_pid = readFile(sshd_pid_file) + if not sshd_pid: return 0 + try: + sshd_pid = int(sshd_pid) + if not sshd_pid: return 0 + if pid_exists(sshd_pid): + return sshd_pid + except: + pass + return 0 + +def get_sshd_pid_of_binfile(): + ''' + @name 通过执行文件获取SSH状态 + @author hwliang + @return int 进程pid + ''' + sshd_bin_list = ['/usr/sbin/sshd', '/usr/bin/sshd','/usr/sbin/ssh','/usr/bin/ssh'] + sshd_bin = None + pid = 0 + for sbin in sshd_bin_list: + if os.path.exists(sbin): + sshd_bin = sbin + break + + if sshd_bin: + pid = get_process_pid(sshd_bin.split('/')[-1],sshd_bin,'-D') + + return pid + def GetSSHStatus(): - if os.path.exists('/usr/bin/apt-get'): - status = ExecShell("service ssh status | grep -P '(dead|stop)'") - else: - import system - panelsys = system.system() - version = panelsys.GetSystemVersion() - if version.find(' 7.') != -1: - status = ExecShell("systemctl status sshd.service | grep 'dead'") - else: - status = ExecShell("/etc/init.d/sshd status | grep -e 'stopped' -e '已停'") - if len(status[0]) > 3: - status = False - else: - status = True - return status + ''' + @name 获取SSH状态 + @author hwliang + @return bool + ''' + if get_sshd_pid_of_pidfile(): + return True + elif get_sshd_pid_of_binfile(): + return True + return False + + +def get_sshd_status(): + ''' + @name 获取SSH状态 + @author hwliang + @return bool + ''' + return GetSSHStatus() + #检查端口是否合法 def CheckPort(port,other=None): @@ -1286,7 +1448,8 @@ def load_module(pluginCode): from BTPanel import cache p_tk = 'data/%s' % md5(pluginCode + get_uuid()) pluginInfo = None - if cache: pluginInfo = cache.get(pluginCode + 'code') + skey = md5(pluginCode+'code') + if cache: pluginInfo = cache.get(skey) if not pluginInfo: import panelAuth pdata = panelAuth.panelAuth().create_serverid(None) @@ -1299,9 +1462,9 @@ def load_module(pluginCode): if not os.path.exists(p_tk): return False pluginInfo = json.loads(ReadFile(p_tk)) if pluginInfo['status'] == False: return False - WriteFile(p_tk, json.dumps(pluginInfo)) - os.chmod(p_tk, 384) - if cache: cache.set(pluginCode + 'code', pluginInfo, 1800) + WriteFile(p_tk,json.dumps(pluginInfo)) + os.chmod(p_tk,384) + if cache: cache.set(skey,pluginInfo,1800) mod = sys.modules.setdefault(pluginCode, new_module(pluginCode)) code = compile(pluginInfo['msg'].encode('utf-8'), pluginCode, 'exec') @@ -1406,6 +1569,34 @@ def process_exists(pname,exe = None,cmdline = None): return False except: return True +def get_process_pid(pname,exe = None,cmdline = None): + ''' + @name 通过进程名获取进程PID + @author hwliang + @param pname 进程名 + @param exe 进程路径 + @param cmdline 进程任意命令行参数 + @return int 返回进程PID + ''' + import psutil + pids = psutil.pids() + for pid in pids: + try: + p = psutil.Process(pid) + if p.name() == pname: + if not exe and not cmdline: + return pid + else: + if exe: + if p.exe() == exe: + if not cmdline: + return pid + return 0 + if cmdline: + if cmdline in p.cmdline(): return pid + except:pass + return 0 + #pid是否存在 def pid_exists(pid): if os.path.exists('/proc/{}/exe'.format(pid)): @@ -1445,23 +1636,50 @@ def to_ord(string): #xss 防御 def xssencode(text): - import cgi - list = ['`', '~', '&', '#', '/', '*', '$', '@', '<', '>', '\"', '\'', ';', '%', ',', '.', '\\u'] - ret = [] - for i in text: - if i in list: - i = '' - ret.append(i) - str_convert = ''.join(ret) - text2 = cgi.escape(str_convert, quote=True) - return text2 + try: + from cgi import html + list=['`','~','&','#','/','*','$','@','<','>','\"','\'',';','%',',','.','\\u'] + ret=[] + for i in text: + if i in list: + i='' + ret.append(i) + str_convert = ''.join(ret) + text2=html.escape(str_convert, quote=True) + return text2 + except: + return text.replace('&', '&').replace('"', '"').replace('<', '<').replace('>', '>') + + +#xss 防御 +def xsssec(text): + return text.replace('&', '&').replace('"', '"').replace('<', '<').replace('>', '>') + + +#xss version +def xss_version(text): + try: + if not text or not isinstance(text,str): return text + text = text.strip() + list=['`','~','&','#','/','*','$','@','<','>','\"','\'',';','%',',','\\u'] + ret=[] + for i in text: + if i in list: + i='' + ret.append(i) + str_convert = ''.join(ret) + return str_convert + except: + return text.replace('&', '&').replace('"', '"').replace('<', '<').replace('>', '>') #xss 防御 def xssencode2(text): - import cgi - text2 = cgi.escape(text, quote=True) - return text2 - + try: + from cgi import html + text2=html.escape(text, quote=True) + return text2 + except: + return text.replace('&', '&').replace('"', '"').replace('<', '<').replace('>', '>') # 取缓存 def cache_get(key): from BTPanel import cache @@ -1498,10 +1716,14 @@ def sess_remove(key): # 构造分页 def get_page(count, p=1, rows=12, callback='', result='1,2,3,4,5,8'): import page - from BTPanel import request - page = page.Page(); - info = {'count': count, 'row': rows, 'p': p, 'return_js': callback, 'uri': request.full_path} - data = {'page': page.GetPage(info, result), 'shift': str(page.SHIFT), 'row': str(page.ROW)} + try: + from BTPanel import request + uri = url_encode(request.full_path) + except: + uri = '' + page = page.Page() + info = { 'count':count, 'row':rows, 'p':p, 'return_js':callback ,'uri':uri} + data = { 'page': page.GetPage(info,result), 'shift': str(page.SHIFT), 'row': str(page.ROW) } return data # 取面板版本 @@ -1527,7 +1749,13 @@ def get_os_version(): @author hwliang<2021-08-07> @return string ''' - version = readFile('/etc/redhat-release') + p_file = '/etc/.productinfo' + if os.path.exists(p_file): + s_tmp = readFile(p_file).split("\n") + if s_tmp[0].find('Kylin') != -1 and len(s_tmp) > 1: + version = s_tmp[0] + ' ' + s_tmp[1].split('/')[0].strip() + else: + version = readFile('/etc/redhat-release') if not version: version = readFile('/etc/issue').strip().split("\n")[0].replace('\\n','').replace('\l','').strip() else: @@ -1537,7 +1765,7 @@ def get_os_version(): version = "{} {}(Py{}.{}.{})".format(version,os.uname().machine,v_info.major,v_info.minor,v_info.micro) except: version = "{} (Py{}.{}.{})".format(version,v_info.major,v_info.minor,v_info.micro) - return version + return xsssec(version) #取文件或目录大小 def get_path_size(path, exclude=[]): @@ -1603,11 +1831,10 @@ def write_request_log(reques = None): try: from BTPanel import request,g,session if session.get('debug') == 1: return - if request.path in ['/service_status','/favicon.ico','/task','/system','/ajax','/control','/data','/ssl']: - return False - log_path = '{}/logs/request'.format(get_panel_path()) log_file = getDate(format='%Y-%m-%d') + '.json' + log_path_backup = '/www/backup/panel/logs' + log_file_backup = getDate(format='%Y-%m-%d') + '.bak' if not os.path.exists(log_path): os.makedirs(log_path) log_data = [] @@ -1617,17 +1844,29 @@ def write_request_log(reques = None): log_data.append(request.full_path) log_data.append(request.headers.get('User-Agent')) if request.method == 'POST': - args = str(request.form.to_dict()) - if len(args) < 2048 and args.find('pass') == -1 and args.find('user') == -1: - log_data.append(args) - else: - log_data.append('{}') + args = request.form.to_dict() + for k in args.keys(): + if k.find('pass') != -1 or k.find('user') != -1: + args[k] = '******' + if len(args[k]) > 4096: + args[k] = args[k][0:1024] + " -- >4096" + log_data.append(str(args)) else: log_data.append('{}') log_data.append(int((time.time() - g.request_time) * 1000)) - WriteFile(log_path + '/' + log_file,json.dumps(log_data) + "\n",'a+') + log_data.append(g.response.status_code) + log_data.append(g.response.content_length) + log_data.append(g.response.headers.get('Content-Type')) + log_data.append(request.headers.get('Host')) + log_data.append(str(reques)) + log_msg = json.dumps(log_data) + "\n" + WriteFile(log_path + '/' + log_file,log_msg,'a+') + if not os.path.exists(log_path_backup): + os.makedirs(log_path_backup,384) + WriteFile(log_path_backup + '/' + log_file_backup,log_msg,'a+') rep_sys_path() - except: pass + except: + pass # 重载模块 def mod_reload(mode): @@ -1646,13 +1885,27 @@ def mod_reload(mode): # 设置权限 def set_mode(filename, mode): if not os.path.exists(filename): return False - mode = int(str(mode), 8) - os.chmod(filename, mode) + mode = int(str(mode),8) + try: + os.chmod(filename,mode) + except: + return False return True +def create_linux_user(user,group): + ''' + @name 创建系统用户 + @author hwliang<2022-01-15> + @param user 用户名 + @param group 所属组 + @return bool + ''' + ExecShell("groupadd {}".format(group)) + ExecShell('useradd -s /sbin/nologin -g {} {}'.format(user,group)) + return True -# 设置用户组 -def set_own(filename, user, group=None): +#设置用户组 +def set_own(filename,user,group=None): if not os.path.exists(filename): return False from pwd import getpwnam try: @@ -1662,8 +1915,13 @@ def set_own(filename, user, group=None): user_info = getpwnam(group) group = user_info.pw_gid except: - # 如果指定用户或组不存在,则使用www - user_info = getpwnam('www') + if user == 'www': create_linux_user(user,group) + #如果指定用户或组不存在,则使用www + try: + user_info = getpwnam('www') + except: + create_linux_user(user,group) + user_info = getpwnam('www') user = user_info.pw_uid group = user_info.pw_gid os.chown(filename, user, group) @@ -1683,8 +1941,8 @@ def path_safe_check(path,force=True): #取数据库字符集 def get_database_character(db_name): try: - import panelMysql - tmp = panelMysql.panelMysql().query("show create database `%s`" % db_name.strip()) + db_obj = get_mysql_obj(db_name) + tmp = db_obj.query("show create database `%s`" % db_name.strip()) c_type = str(re.findall(r"SET\s+([\w\d-]+)\s",tmp[0][1])[0]) c_types = ['utf8','utf-8','gbk','big5','utf8mb4'] if not c_type.lower() in c_types: return 'utf8' @@ -1692,6 +1950,66 @@ def get_database_character(db_name): except: return 'utf8' +# 取mysql数据库对象 +def get_mysql_obj(db_name): + is_cloud_db = False + if db_name: + db_find = M('databases').where("name=?" ,db_name).find() + if db_find['sid']: + return get_mysql_obj_by_sid(db_find['sid']) + is_cloud_db = db_find['db_type'] in ['1',1] + if is_cloud_db: + import db_mysql + db_obj = db_mysql.panelMysql() + conn_config = json.loads(db_find['conn_config']) + try: + db_obj = db_obj.set_host(conn_config['db_host'],conn_config['db_port'],conn_config['db_name'],conn_config['db_user'],conn_config['db_password']) + except Exception as e: + raise PanelError(GetMySQLError(e)) + else: + import panelMysql + db_obj = panelMysql.panelMysql() + return db_obj + +# 取mysql数据库对像 By sid +def get_mysql_obj_by_sid(sid = 0,conn_config = None): + if sid in ['0','']: sid = 0 + if sid: + if not conn_config: conn_config = M('database_servers').where("id=?" ,sid).find() + import db_mysql + db_obj = db_mysql.panelMysql() + try: + db_obj = db_obj.set_host(conn_config['db_host'],conn_config['db_port'],None,conn_config['db_user'],conn_config['db_password']) + except Exception as e: + raise PanelError(GetMySQLError(e)) + else: + import panelMysql + db_obj = panelMysql.panelMysql() + return db_obj + +def GetMySQLError(e): + res = '' + if e.args[0] == 1045: + res = get_msg_gettext('Database username or password is wrong!') + if e.args[0] == 1049: + res = get_msg_gettext('database does not exist!') + if e.args[0] == 1044: + res = get_msg_gettext('No permission, or the specified database does not exist!') + if e.args[0] == 1062: + res = get_msg_gettext('Database already exists!') + if e.args[0] == 1146: + res = get_msg_gettext('Table does not exist!') + if e.args[0] == 2003: + res = get_msg_gettext('Database server connection failed!') + if e.args[0] == 1142: + res = get_msg_gettext('Insufficient user rights!') + if res: + res = res + "
                                      " + str(e) + "
                                      " + else: + res = str(e) + return res + + def get_database_codestr(codeing): wheres = { 'utf8' : 'utf8_general_ci', @@ -1702,14 +2020,14 @@ def get_database_codestr(codeing): return wheres[codeing] -def get_database_size(): +def get_database_size(name=None): """ @获取数据库大小 """ data = {} try: - import panelMysql - tables = panelMysql.panelMysql().query("select table_schema, (sum(DATA_LENGTH)+sum(INDEX_LENGTH)) as data from information_schema.TABLES group by table_schema") + mysql_obj = get_mysql_obj(name) + tables = mysql_obj.query("select table_schema, (sum(DATA_LENGTH)+sum(INDEX_LENGTH)) as data from information_schema.TABLES group by table_schema") if type(tables) == list: for x in tables: if len(x) < 2:continue @@ -1718,6 +2036,35 @@ def get_database_size(): except: return data return data +def get_database_size_by_name(name): + """ + @获取数据库大小 + """ + data = 0 + try: + mysql_obj = get_mysql_obj(name) + tables = mysql_obj.query("select table_schema, (sum(DATA_LENGTH)+sum(INDEX_LENGTH)) as data from information_schema.TABLES WHERE table_schema='{}' group by table_schema".format(name)) + data = tables[0][1] + if not data: data = 0 + except: return data + return data + +def get_database_size_by_id(id): + """ + @获取数据库大小 + """ + data = 0 + try: + name = M('databases').where('id=?',id).getField('name') + mysql_obj = get_mysql_obj(name) + tables = mysql_obj.query("select table_schema, (sum(DATA_LENGTH)+sum(INDEX_LENGTH)) as data from information_schema.TABLES WHERE table_schema='{}' group by table_schema".format(name)) + data = tables[0][1] + if not data: data = 0 + except: + return data + return data + + def en_punycode(domain): if sys.version_info[0] == 2: domain = domain.encode('utf8') @@ -1817,6 +2164,35 @@ def get_limit_ip(): return iplong_list +def is_api_limit_ip(ip_list,client_ip): + ''' + @name 判断IP是否在限制列表中 + @author hwliang<2022-02-10> + @param ip_list 限制IP列表 + @param client_ip 客户端IP + @return bool + ''' + iplong_list = [] + for limit_ip in ip_list: + if not limit_ip: continue + if limit_ip in ['*','all','0.0.0.0','0.0.0.0/0','0.0.0.0/24','0.0.0.0/32']: return True + limit_ip = limit_ip.split('-') + iplong = {} + iplong['min'] = ip2long(limit_ip[0]) + if len(limit_ip) > 1: + iplong['max'] = ip2long(limit_ip[1]) + else: + iplong['max'] = iplong['min'] + iplong_list.append(iplong) + + client_ip_long = ip2long(client_ip) + for limit_ip in iplong_list: + if client_ip_long >= limit_ip['min'] and client_ip_long <= limit_ip['max']: + return True + return False + + + #检查IP白名单 @@ -1829,12 +2205,12 @@ def check_ip_panel(): for limit_ip in iplong_list: if client_ip_long >= limit_ip['min'] and client_ip_long <= limit_ip['max']: return False - return 404 - # 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 + + 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 error_not_login(errorStr,True) #检查面板域名 def check_domain_panel(): @@ -1844,12 +2220,11 @@ def check_domain_panel(): client_ip = GetClientIp() if client_ip in ['127.0.0.1','localhost','::1']: return False if tmp.strip().lower() != domain.strip().lower(): - return 404 - # 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:pass - # return errorStr + 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:pass + return error_not_login(errorStr,True) return False #是否离线模式 @@ -1865,22 +2240,46 @@ def auto_backup_panel(): paths = panel_paeh + '/data/not_auto_backup.pl' if os.path.exists(paths): return False b_path = '{}/panel'.format(get_backup_path()) - backup_path = b_path + '/' + format_date('%Y-%m-%d') - if os.path.exists(backup_path): return True - if os.path.getsize(panel_paeh + '/data/default.db') > 104857600 * 2: return False + day_date = format_date('%Y-%m-%d') + backup_path = b_path + '/' + day_date + backup_file = backup_path + '.zip' + if os.path.exists(backup_path) or os.path.exists(backup_file): return True + ignore_default = '' + ignore_system = '' + max_size = 100 * 1024 * 1024 + if os.path.getsize('{}/data/default.db'.format(panel_paeh)) > max_size: + ignore_default = 'default.db' + if os.path.getsize('{}/data/system.db'.format(panel_paeh)) > max_size: + ignore_system = 'system.db' os.makedirs(backup_path,384) import shutil - shutil.copytree(panel_paeh + '/data',backup_path + '/data') + shutil.copytree(panel_paeh + '/data',backup_path + '/data',ignore = shutil.ignore_patterns(ignore_system,ignore_default)) shutil.copytree(panel_paeh + '/config',backup_path + '/config') shutil.copytree(panel_paeh + '/vhost',backup_path + '/vhost') - ExecShell("chmod -R 600 {path};chown -R root.root {path}".format(path=b_path)) + ExecShell("cd {} && zip {} -r {}/".format(b_path,backup_file,day_date)) + ExecShell("chmod -R 600 {path};chown -R root.root {path}".format(path=backup_file)) + if os.path.exists(backup_path): shutil.rmtree(backup_path) + time_now = time.time() - (86400 * 15) for f in os.listdir(b_path): if time.mktime(time.strptime(f, "%Y-%m-%d")) < time_now: path = b_path + '/' + f - if os.path.exists(path): shutil.rmtree(path) - except: - pass + b_file = path + '.zip' + if os.path.exists(path): + shutil.rmtree(path) + if os.path.exists(b_file): + os.remove(b_file) + set_php_cli_env() + except:pass + + + +def set_php_cli_env(): + ''' + @name 重新设置php-cli.ini配置 + ''' + import jobs + jobs.set_php_cli_env() @@ -2067,7 +2466,7 @@ def get_php_version_conf(conf): ''' if not conf: return '00' if conf.find('enable-php-') != -1: - rep = r"enable-php-(\w{2,5})\.conf" + rep = r"enable-php-(\w{2,5})[-\w]*\.conf" tmp = re.findall(rep,conf) if not tmp: return '00' elif conf.find('/usr/local/lsws/lsphp') != -1: @@ -2134,8 +2533,10 @@ def sub_php_address(conf_file,rep,tsub,php_version): conf = readFile(conf_file) if not conf: return False #if conf.find('#PHP') == -1 and conf.find('pathinfo.conf') == -1: return False - phpv = get_php_version_conf(conf) - if phpv != php_version: return False + if conf_file.split('-')[-1].find(php_version+".") != 0: + phpv = get_php_version_conf(conf) + if phpv != php_version: return False + tmp = re.search(rep,conf) if not tmp: return False if tmp.group() == tsub: return False @@ -2150,7 +2551,7 @@ def sync_all_address(): @author hwliang<2020-10-24> @return void ''' - php_versions = ['52','53','54','55','56','70','71','72','73','74','75','80','81'] + php_versions = get_php_versions() for phpv in php_versions: sync_php_address(phpv) @@ -2163,8 +2564,8 @@ def sync_php_address(php_version): ''' if not os.path.exists('{}/php/{}/bin/php'.format(get_setup_path(),php_version)): # 指定PHP版本是否安装 return False - ngx_rep = r"(unix:/tmp/php-cgi.*\.sock|127.0.0.1:\d+)" - apa_rep = r"(unix:/tmp/php-cgi.*\.sock\|fcgi://localhost|fcgi://127.0.0.1:\d+)" + ngx_rep = r"(unix:/tmp/php-cgi.*\.sock|\d+\.\d+\.\d+\.\d+:\d+)" + apa_rep = r"(unix:/tmp/php-cgi.*\.sock\|fcgi://localhost|fcgi://\d+\.\d+\.\d+\.\d+:\d+)" ngx_proxy = get_php_proxy(php_version,'nginx') apa_proxy = get_php_proxy(php_version,'apache') is_write = False @@ -2204,20 +2605,22 @@ def sync_php_address(php_version): def url_encode(data): - if type(data) == str: return data - import urllib + if type(data) != str: return data if sys.version_info[0] != 2: - pdata = urllib.parse.urlencode(data).encode('utf-8') + import urllib.parse + pdata = urllib.parse.quote(data) else: + import urllib pdata = urllib.urlencode(data) return pdata def url_decode(data): - if type(data) == str: return data - import urllib + if type(data) != str: return data if sys.version_info[0] != 2: - pdata = urllib.parse.urldecode(data).encode('utf-8') + import urllib.parse + pdata = urllib.parse.unquote(data) else: + import urllib pdata = urllib.urldecode(data) return pdata @@ -2283,7 +2686,7 @@ def set_cdn_url(cdn_url): return True def get_python_bin(): - bin_file = '{}/pyenv/bin/python'.format(get_panel_path()) + bin_file = '{}/pyenv/bin/python3'.format(get_panel_path()) if os.path.exists(bin_file): return bin_file return '/usr/bin/python' @@ -2447,8 +2850,7 @@ def get_login_token(): return token_s def get_sess_key(): - from BTPanel import session - return md5(get_login_token() + session.get('request_token_head','')) + return md5(get_login_token() + get_csrf_sess_html_token_value()) def password_salt(password,username=None,uid=None): @@ -2696,6 +3098,9 @@ def run_thread(fun,args = (),daemon=False): def check_domain_cloud(domain): run_thread(cloud_check_domain,(domain,)) +def count_wp(): + run_thread(httpPost('https://brandnew.aapanel.com/api/setupCount/setupWP',{})) + def cloud_check_domain(domain): ''' @name 从云端验证域名的可访问性,并将结果保存到文件 @@ -2765,15 +3170,26 @@ def send_file(data,fname='',mimetype = ''): if not mimetype: mimetype = "application/octet-stream" if not fname: fname = 'doan.txt' - return send_to(fp, - mimetype=mimetype, - as_attachment=True, - add_etags=True, - conditional=True, - attachment_filename=fname, - cache_timeout=0) + import flask + if flask.__version__ < "2.1.0": + return send_to(fp, + mimetype=mimetype, + as_attachment=True, + add_etags=True, + conditional=True, + attachment_filename=fname, + cache_timeout=0) + else: + return send_to(fp, + mimetype=mimetype, + as_attachment=True, + etag=True, + conditional=True, + download_name=fname, + max_age=0) def gen_password(length=8,chars=string.ascii_letters+string.digits): + from random import choice return ''.join([choice(chars) for i in range(length)]) def get_ipaddress(): @@ -2876,28 +3292,45 @@ def arequests(method,url,data=None,timeout=3): threading.Thread(target=method, args=(url,data,timeout)).start() #取通用对象 +re_key_match = re.compile(r'^[\w\s\[\]\-\.]+$') +re_key_match2 = re.compile(r'^\.?__[\w\s[\]\-]+__\.?$') +key_filter_list = ['get','set','get_items','exists','__contains__','__setitem__','__getitem__','__delitem__','__delattr__','__setattr__','__getattr__','__class__'] class dict_obj: def __contains__(self, key): return getattr(self,key,None) - def __setitem__(self, key, value): setattr(self,key,value) + def __setitem__(self, key, value): + if key in key_filter_list: + raise PanelError("wrong field name") + if not re_key_match.match(key) or re_key_match2.match(key): + raise PanelError("wrong field name") + 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 def exists(self,keys): return exists_args(keys,self) + def set(self,key,value): + if not isinstance(value,str) or not isinstance(key,str): return False + if key in key_filter_list: + raise PanelError("wrong field name") + if not re_key_match.match(key) or re_key_match2.match(key): + raise PanelError("wrong field name") + return setattr(self,key,value) + def get(self,key,default='',format='',limit = []): ''' @name 获取指定参数 @param key 参数名称,允许在/后面限制参数格式,请参考参数值格式(format) @param default 默认值,默认空字符串 - @param format 参数值格式(int|str|float|json|xss|path|url|ip|ipv4|ipv6|letter|mail|phone|正则表达式|>1|<1|=1),默认为空 + @param format 参数值格式(int|str|port|float|json|xss|path|url|ip|ipv4|ipv6|letter|mail|phone|正则表达式|>1|<1|=1),默认为空 @param limit 限制参数值内容 @param return mixed ''' if key.find('/') != -1: key,format = key.split('/') - result = getattr(self,key,default).strip() + result = getattr(self,key,default) + if isinstance(result,str): result = result.strip() if format: if format in ['str','string','s']: result = str(result) @@ -2933,10 +3366,10 @@ class dict_obj: if not re.match(regex,result): raise ValueError('Parameters: {}, the correct URL format is required'.format(key)) elif format in ['ip','ipaddr','i','ipv4','ipv6']: - if format is 'ipv4': + if format == 'ipv4': if not is_ipv4(result): raise ValueError('Parameters: {}, the correct ipv4 address is required'.format(key)) - elif format is 'ipv6': + elif format == 'ipv6': if not is_ipv6(result): raise ValueError('Parameters: {}, the correct ipv6 address is required'.format(key)) else: @@ -2961,10 +3394,10 @@ class dict_obj: length = int(format[1:].strip()) result_len = len(result) error_obj = ValueError("Parameters: {}, the required length is {}".format(key,format)) - if operator is '=': + if operator == '=': if result_len != length: raise error_obj - elif operator is '>': + elif operator == '>': if result_len < length: raise error_obj else: @@ -3073,7 +3506,7 @@ def check_app(check='app'): except: return False elif check=='app_bind': - if not cache_get('get_bind_status'):return False + if not cache_get(Md5(os.uname().version)):return False if not os.path.exists("/www/server/panel/plugin/btapp/btapp_main.py"):return False if not os.path.exists(path + 'config/api.json'):return False btapp_info = json.loads(readFile(path +'config/api.json')) @@ -3086,7 +3519,6 @@ def check_app(check='app'): if not app_info: return False return True - #宝塔邮件报警 def send_mail(title,body,is_logs=False,is_type="aapanel login reminder"): if is_logs: @@ -3252,7 +3684,7 @@ def get_sys_path(): @return tuple ''' a = ['/www','/usr','/','/dev','/home','/media','/mnt','/opt','/tmp','/var'] - c = ['/www/Recycle_bin/','/www/backup/','/www/php_session/','/www/wwwlogs/','/www/server/','/etc/','/usr/','/var/','/boot/','/proc/','/sys/','/tmp/','/root/','/lib/','/bin/','/sbin/','/run/','/lib64/','/lib32/','/srv/'] + c = ['/www/.Recycle_bin/','/www/backup/','/www/php_session/','/www/wwwlogs/','/www/server/','/etc/','/usr/','/var/','/boot/','/proc/','/sys/','/tmp/','/root/','/lib/','/bin/','/sbin/','/run/','/lib64/','/lib32/','/srv/'] return a,c @@ -3263,18 +3695,23 @@ def check_site_path(site_path): @param site_path 网站根目录全路径 @return bool ''' - whites = ['/www/server/tomcat','/www/server/stop','/www/server/phpmyadmin'] - for w in whites: - if site_path.find(w) == 0: return True - a,error_paths = get_sys_path() - site_path = site_path.strip() - if site_path[-1] == '/': site_path = site_path[:-1] - if site_path in a: + try: + if site_path in ['/','/usr','/dev','/home','/media','/mnt','/opt','/tmp','/var']: + return False + whites = ['/www/server/tomcat','/www/server/stop','/www/server/phpmyadmin'] + for w in whites: + if site_path.find(w) == 0: return True + a,error_paths = get_sys_path() + site_path = site_path.strip() + if site_path[-1] == '/': site_path = site_path[:-1] + if site_path in a: + return False + site_path += '/' + for ep in error_paths: + if site_path.find(ep) == 0: return False + return True + except: return False - site_path += '/' - for ep in error_paths: - if site_path.find(ep) == 0: return False - return True def is_debug(): debug_file = "{}/data/debug.pl".format(get_panel_path()) @@ -3290,7 +3727,190 @@ class PanelError(Exception): self.value = value def __str__(self): - return ("面板运行时发生错误: {}".format(repr(self.value))) + return ("An error occurred while the panel was running: {}".format(str(self.value))) + + +def sys_path_append(path): + ''' + @name 追加引用路径 + @author hwliang<2021-07-07> + @param path 路径 + @return void + ''' + try: + if not path in sys.path: + sys.path.insert(0,path) + except: pass + + +def get_plugin_find(upgrade_plugin_name = None): + ''' + @name 获取指定软件信息 + @author hwliang<2021-06-15> + @param upgrade_plugin_name 插件名称 + @return dict + ''' + import panelPlugin + plugin_list_data = panelPlugin.panelPlugin().get_cloud_list() + + for p_data_info in plugin_list_data['list']: + if p_data_info['name'] == upgrade_plugin_name: + upgrade_plugin_name = p_data_info['name'] + return p_data_info + + return get_plugin_info(upgrade_plugin_name) + + +def get_plugin_value(plugin_name,key): + ''' + @name 获取插件配置值 + @author hwliang + @param plugin_name 插件名称 + @param key 字段名 + @return mixed + ''' + plugin_info = get_plugin_find(plugin_name) + return plugin_info.get(key,None) + +def get_plugin_pid(plugin_name): + ''' + @name 获取指定插件的pid + @author hwliang<2021-06-15> + @param plugin_name 插件名称 + @return string + ''' + plugin_info = get_plugin_find(plugin_name) + if not plugin_info: return 0 + if 'pid' in plugin_info: + return plugin_info['pid'] + return 0 + + +def get_plugin_info(upgrade_plugin_name): + ''' + @name 获取插件信息 + @author hwliang<2021-06-15> + @param upgrade_plugin_name 插件名称 + @return dict + ''' + plugin_path = get_plugin_path() + plugin_info_file = '{}/{}/info.json'.format(plugin_path,upgrade_plugin_name) + if not os.path.exists(plugin_info_file): return {} + info_body = readFile(plugin_info_file) + if not info_body: return {} + plugin_info = json.loads(info_body) + return plugin_info + +def download_main(upgrade_plugin_name,upgrade_version): + ''' + @name 下载插件主程序文件 + @author hwliang<2021-06-25> + @param upgrade_plugin_name 插件名称 + @param upgrade_version 插件版本 + @return void + ''' + import requests,shutil + plugin_path = get_plugin_path() + tmp_path = '{}/temp'.format(get_panel_path()) + download_d_main_url = 'https://api.bt.cn/down/download_plugin_main' + pdata = get_user_info() + pdata['name'] = upgrade_plugin_name + pdata['version'] = upgrade_version + pdata['os'] = 'Linux' + import config,socket + import requests.packages.urllib3.util.connection as urllib3_conn + _ip_type = config.config().get_request_iptype() + old_family = urllib3_conn.allowed_gai_family + if _ip_type == 'ipv4': + urllib3_conn.allowed_gai_family = lambda: socket.AF_INET + elif _ip_type == 'ipv6': + urllib3_conn.allowed_gai_family = lambda: socket.AF_INET6 + try: + download_res = requests.post(download_d_main_url,pdata,timeout=30,headers=get_requests_headers()) + except Exception as ex: + raise PanelError(error_conn_cloud(str(ex))) + finally: + urllib3_conn.allowed_gai_family = old_family + filename = '{}/{}.py'.format(tmp_path,upgrade_plugin_name) + with open(filename,'wb+') as save_script_f: + save_script_f.write(download_res.content) + save_script_f.close() + + if md5(download_res.content) != download_res.headers.get('Content-md5'): + raise PanelError('Plug-in installation package HASH verification failed.') + dst_file = '{plugin_path}/{plugin_name}/{plugin_name}_main.py'.format(plugin_path=plugin_path,plugin_name = upgrade_plugin_name) + shutil.copyfile(filename,dst_file) + if os.path.exists(filename): os.remove(filename) + WriteLog('Software manager',"Plugin [{}] program file exception detected, auto-repair has been attempted!".format(get_plugin_info(upgrade_plugin_name)['title'])) + + +def get_plugin_bin(plugin_save_file,plugin_timeout): + list_body = None + plugin_timeout = 86400 + if not os.path.exists(plugin_save_file): return list_body + s_time = time.time() + m_time = os.stat(plugin_save_file).st_mtime + if (s_time - m_time) < plugin_timeout or not plugin_timeout: + list_body = readFile(plugin_save_file,'rb') + return list_body + + +def get_plugin_main_object(plugin_name,sys_path): + os_file = sys_path + '/' + plugin_name + '_main.so' + php_file = sys_path + '/index.php' + is_php = False + plugin_obj = None + if os.path.exists(os_file): # 是否为编译后的so文件 + try: + plugin_obj = __import__(plugin_name + '_main') + except Exception as ex: + if str(ex).find('undefined symbol') != -1: + re_download_main(plugin_name,get_plugin_path()) + writeFile("{}/data/{}.pl".format(get_panel_path(),plugin_name),'1') + raise PanelError('The plug-in program file is abnormal, an attempt has been made to automatically repair it, please refresh the page and try again!') + elif os.path.exists(php_file): # 是否为PHP代码 + import panelPHP + is_php = True + plugin_obj = panelPHP.panelPHP(plugin_name) + + return plugin_obj,is_php + + +def get_plugin_script(plugin_name,plugin_path): + # 优先运行py文件 + plugin_file = '{plugin_path}/{name}/{name}_main.py'.format(plugin_path =plugin_path, name=plugin_name) + if not os.path.exists(plugin_file): return '',True + plugin_body = readFile(plugin_file,'rb') + + if plugin_body.find(b'import') != -1: + return plugin_body,True + + # 重新下载空文件 + if not plugin_body.strip(): + re_download_main(plugin_name,get_plugin_path()) + writeFile("{}/data/{}.pl".format(get_panel_path(),plugin_name),'1') + plugin_body = readFile(plugin_file,'rb') + if plugin_body.find(b'import') != -1: return plugin_body,True + + plugin_file_so = '{plugin_path}/{name}/{name}_main.so'.format(plugin_path =plugin_path, name=plugin_name) + if len(plugin_body) > 1024 and plugin_body.find(b'+') != -1 and plugin_body.find(b'/') != -1: + plugin_path = get_plugin_path(plugin_name) + up_file = '{}/upgrade.sh'.format(plugin_path) + if os.path.exists(up_file) and os.path.exists(plugin_file_so): # 删除无效的so文件 + ExecShell("rm -f {}/*.so".format(plugin_path)) + return plugin_body,False + + # 无py文件再运行so + if os.path.exists(plugin_file_so): return '',True + + return plugin_body,False + +def re_download_main(plugin_name,plugin_path): + plugin_file = '{plugin_path}{name}/{name}_main.py'.format(plugin_path =plugin_path, name=plugin_name) + version = get_plugin_info(plugin_name)['versions'] + download_main(plugin_name,version) + plugin_body = readFile(plugin_file,'rb') + return plugin_body def get_sysbit(): ''' @@ -3389,14 +4009,14 @@ def read_config(config_name,ext_name = 'json'): ''' config_file = "{}/config/{}.{}".format(get_panel_path(),config_name,ext_name) if not os.path.exists(config_file): - raise PanelError('指定配置文件{} 不存在'.format(config_name)) + raise PanelError('The specified configuration file {} does not exist'.format(config_name)) config_str = readFile(config_file) if ext_name == 'json': try: config_body = json.loads(config_str) except Exception as ex: - raise PanelError('配置文件不是标准的可解析JSON内容!\n{}'.format(ex)) + raise PanelError('Configuration files are not standard parsable JSON content!\n{}'.format(ex)) return config_body return config_str @@ -3415,7 +4035,7 @@ def save_config(config_name,config_body,ext_name = 'json'): try: config_body = json.dumps(config_body) except Exception as ex: - raise PanelError('配置内容无法被转换为json格式!\n{}'.format(ex)) + raise PanelError('The configuration content cannot be converted to json format!\n{}'.format(ex)) return writeFile(config_file,config_body) @@ -3458,10 +4078,10 @@ def return_data(status,data = {},status_code=None,error_msg = None): @return dict ''' - if status_code is None: + if status_code == None: status_code = 1 if status else 0 - if error_msg is None: - error_msg = '' if status else '未知错误' + if error_msg == None: + error_msg = '' if status else 'unknown error' result = { 'status':status, @@ -3481,6 +4101,7 @@ def return_error(error_msg,status_code = -1,data = []): @param data 响应数据 @return dict ''' + if not data: data = error_msg return return_data(False,data,status_code,str(error_msg)) @@ -3493,6 +4114,7 @@ def error(error_msg,status_code = -1,data = []): @param data 响应数据 @return dict ''' + if not data: data = error_msg return return_error(error_msg,status_code,data) def success(data = [],status_code = 1,error_msg = ''): @@ -3640,7 +4262,7 @@ def get_session_timeout(): from BTPanel import cache skey = 'session_timeout' session_timeout = cache.get(skey) - if not session_timeout is None: return session_timeout + if not session_timeout == None: return session_timeout sess_out_path = '{}/data/session_timeout.pl'.format(get_panel_path()) session_timeout = 86400 @@ -3663,7 +4285,7 @@ def get_login_token_auth(): from BTPanel import cache skey = 'login_token' login_token = cache.get(skey) - if not login_token is None: return login_token + if not login_token == None: return login_token login_token_file = '{}/data/login_token.pl'.format(get_panel_path()) login_token = '1234567890' @@ -3713,6 +4335,11 @@ def to_date(format = "%Y-%m-%d %H:%M:%S",times = None): @param times 时间 @return int ''' + if times: + if isinstance(times,int): return times + if isinstance(times,float): return int(times) + if re.match("^\d+$",times): return int(times) + else: return 0 ts = time.strptime(times, "%Y-%m-%d %H:%M:%S") return time.mktime(ts) @@ -3741,8 +4368,34 @@ def is_apache_nginx(): setup_path = get_setup_path() return os.path.exists(setup_path + '/apache') or os.path.exists(setup_path + '/nginx') +def error_not_login(e = None,_src = None): + ''' + @name 未登录时且未输入正确的安全入口时的响应 + @author hwliang<2021-12-16> + @return Response + ''' + from BTPanel import Response,render_template,redirect + + try: + abort_code = read_config('abort') + if not abort_code in [None,1,0,'0','1']: + if abort_code == 404: return error_404(e) + if abort_code == 403: return error_403(e) + return Response(status=int(abort_code)) + except: + pass + + if e in ['/login']: + return redirect(e) + + if _src: + return e + else: + return render_template('autherr.html') + def error_403(e): - from BTPanel import Response + from BTPanel import Response,session + # if not session.get('login',None): return error_not_login() errorStr = ''' 403 Forbidden @@ -3756,7 +4409,8 @@ def error_403(e): return Response(errorStr, status=403, headers=headers) def error_404(e): - from BTPanel import Response + from BTPanel import Response,session + # if not session.get('login',None): return error_not_login() errorStr = ''' 404 Not Found @@ -3769,3 +4423,1247 @@ def error_404(e): } return Response(errorStr, status=404, headers=headers) + +def get_password_config(): + ''' + @name 获取密码安全配置 + @author hwliang<2021-10-18> + @return int + ''' + import config + return config.config().get_password_config(None) + + +def password_expire_check(): + ''' + @name 密码过期检查 + @author hwliang<2021-10-18> + @return bool + ''' + p_config = get_password_config() + if p_config['expire'] == 0: return True + if time.time() > p_config['expire_time']: return False + return True + +def stop_status_mvore(): + flag=False + try: + nginx_path='/www/server/panel/vhost/nginx/btwaf.conf' + if os.path.exists(nginx_path): + ExecShell('mv %s %s.bak'%(nginx_path,nginx_path)) + flag=True + nginx_path='/www/server/panel/vhost/nginx/free_waf.conf' + if os.path.exists(nginx_path): + ExecShell('mv %s %s.bak'%(nginx_path,nginx_path)) + flag=True + apache_path='/www/server/panel/vhost/apache/btwaf.conf' + if os.path.exists(apache_path): + ExecShell('chattr -i %s && mv %s %s.bak'%(apache_path,apache_path,apache_path)) + flag=True + if flag: + serviceReload() + except: + pass + +def is_error_path(): + if os.path.exists("/www/server/panel/data/error_pl.pl"): + stop_status_mvore() + return True + return False + +def get_php_versions(reverse=False): + ''' + @name 取PHP版本列表 + @author hwliang<2021-12-16> + @param reverse 是否降序 + @return list + ''' + _file = get_panel_path() + '/config/php_versions.json' + if os.path.exists(_file): + version_list = json.loads(readFile(_file)) + else: + version_list = ['52','53','54','55','56','70','71','72','73','74','80','81','82','83','84'] + + return sorted(version_list,reverse=reverse) + +def get_full_session_file(): + ''' + @name 获取临时SESSION文件 + @author hwliang<2021-12-28> + @return string + ''' + from BTPanel import app + full_session_key = app.config['SESSION_KEY_PREFIX'] + get_session_id() + sess_path = get_panel_path() + '/data/session/' + return sess_path + '/' + md5(full_session_key) + + + +def install_mysql_client(): + ''' + @name 安装mysql客户端 + @author hwliang<2022-01-14> + @return void + ''' + if os.path.exists('/usr/bin/yum'): + os.system("yum install mariadb -y") + if not os.path.exists('/usr/bin/mysql'): + os.system("yum reinstall mariadb -y") + elif os.path.exists('/usr/bin/apt-get'): + os.system('apt-get install mariadb-client -y') + if not os.path.exists('/usr/bin/mysql'): + os.system('apt-get reinstall mariadb-client* -y') + +def get_mysqldump_bin(): + ''' + @name 获取mysqldump路径 + @author hwliang<2022-01-14> + @return string + ''' + bin_files = [ + '{}/mysql/bin/mysqldump'.format(get_setup_path()), + '/usr/bin/mysqldump', + '/usr/local/bin/mysqldump', + '/usr/sbin/mysqldump', + '/usr/local/sbin/mysqldump' + ] + + for bin_file in bin_files: + if os.path.exists(bin_file): + return bin_file + + install_mysql_client() + + for bin_file in bin_files: + if os.path.exists(bin_file): + return bin_file + + return bin_files[0] + +def get_mysql_bin(): + ''' + @name 获取mysql路径 + @author hwliang<2022-01-14> + @return string + ''' + bin_files = [ + '{}/mysql/bin/mysql'.format(get_setup_path()), + '/usr/bin/mysql', + '/usr/local/bin/mysql', + '/usr/sbin/mysql', + '/usr/local/sbin/mysql' + ] + + for bin_file in bin_files: + if os.path.exists(bin_file): + return bin_file + + install_mysql_client() + + for bin_file in bin_files: + if os.path.exists(bin_file): + return bin_file + return bin_files[0] + +def error_conn_cloud(text): + ''' + @name 连接云端失败 + @author hwliang<2021-12-18> + @return void + ''' + code_msg = '' + if text.find("502 Bad Gateway") != -1: + code_msg = '502 Bad Gateway' + if text.find("504 Bad Gateway") != -1: + code_msg = '504 Bad Gateway' + elif text.find("Connection refused") != -1: + code_msg = 'Connection refused' + elif text.find("Connection timed out") != -1: + code_msg = 'Connection timed out' + elif text.find("Connection reset by peer") != -1: + code_msg = 'Connection reset by peer' + elif text.find("Name or service not known") != -1: + code_msg = 'Name or service not known' + elif text.find("No route to host") != -1: + code_msg = 'No route to host' + elif text.find("No such file or directory") != -1: + code_msg = 'No such file or directory' + elif text.find("404 Not Found") != -1: + code_msg = '404 Not Found' + elif text.find("403 Forbidden") != -1: + code_msg = '403 Forbidden' + elif text.find("401 Unauthorized") != -1: + code_msg = '401 Unauthorized' + elif text.find("400 Bad Request") != -1: + code_msg = '400 Bad Request' + elif text.find("Remote end closed connection without response") != -1: + code_msg = 'Remote end closed connection' + err_template_file = '{}/BTPanel/templates/default/error_connect.html'.format(get_panel_path()) + msg = readFile(err_template_file) + msg = msg.format(code=code_msg) + return PanelError(msg) + +def get_mountpoint_list(): + ''' + @name 获取挂载点列表 + @author hwliang<2021-12-18> + @return list + ''' + import psutil + mount_list = [] + for mount in psutil.disk_partitions(): + mountpoint = mount.mountpoint if mount.mountpoint[-1] == '/' else mount.mountpoint + '/' + mount_list.append(mountpoint) + # 根据挂载点字符长度排序 + mount_list.sort(key=lambda i: len(i), reverse=True) + return mount_list + + +def get_path_in_mountpoint(path): + ''' + @name 获取文件或目录目录所在挂载点 + @author hwliang<2022-03-30> + @param path 文件或目录路径 + @return string + ''' + # 判断是否是绝对路径 + if path.find('./') != -1 or path[0] != '/': raise PanelError("不能使用相对路径") + if not path: raise PanelError("文件或目录路径不能为空") + + # 在目录尾加/ + if os.path.isdir(path): + path = path if path[-1] == '/' else path + '/' + + # 匹配挂载点 + mount_list = get_mountpoint_list() + for mountpoint in mount_list: + if path.startswith(mountpoint): + return mountpoint + + # 没有匹配到挂载点 + return '/' + +def get_recycle_bin_path(path): + ''' + @name 获取指定文件或目录的回收站路径 + @author hwliang<2022-03-30> + @param path 文件或目录路径 + @return string + ''' + mountpoint = get_path_in_mountpoint(path) + recycle_bin_path = '{}/.Recycle_bin/'.format(mountpoint) + try: + if not os.path.exists(recycle_bin_path): + os.mkdir(recycle_bin_path, 384) + except: + return '/www/.Recycle_bin/' + return recycle_bin_path + + +def get_recycle_bin_list(): + ''' + @name 获取回收站列表 + @author hwliang<2022-03-30> + @return list + ''' + # 旧的回收站重命名为.Recycle_bin + default_path = '/www/.Recycle_bin' + default_path_src = '/www/Recycle_bin' + if os.path.exists(default_path_src) and not os.path.exists(default_path): + try: + os.rename(default_path_src,default_path) + except: + ExecShell("mv {} {}".format(default_path_src,default_path)) + + if not os.path.exists(default_path): + os.makedirs(default_path,384) + + # 获取回收站列表 + recycle_bin_list = [] + mtime_list = [] # 修改时间 + for mountpoint in get_mountpoint_list(): + recycle_bin_path = '{}.Recycle_bin/'.format(mountpoint) + + try: + if not os.path.exists(recycle_bin_path): + os.mkdir(recycle_bin_path,384) + if not os.path.exists(recycle_bin_path): continue + mtime = os.path.getmtime(recycle_bin_path) + if mtime in mtime_list: continue # 通过修改时间去重 + mtime_list.append(mtime) + recycle_bin_list.append(recycle_bin_path) + except: + continue + + # 包含默认回收站路径? + if not default_path + '/' in recycle_bin_list: + recycle_bin_list.append(default_path + '/') + + return recycle_bin_list + + +def check_password(password): + """ + 密码强度: + 0 弱 + 1 中 + 2 强 + """ + l = 0 + low = False + up = False + symbol = False + digit = False + p_len = len(password) + if p_len < 8: + return l + for i in password: + if i.islower(): + low = True + if i.isupper(): + up = True + if i in ['~','!','@','#','$','%','^','&','*','(',')','_','-','=','+','<','>',',','.','/','"','|','\\',"'",'?']: + symbol = True + if i.isdigit(): + digit = True + # 判断重复出现 + tmp = len(set([i for i in password])) + if tmp >= 2: + if low and up and symbol and digit: + l = 2 + if p_len >= 11: + l = 1 + return l + +def set_module_logs(mod_name, fun_name, count=1): + """ + @模块使用次数 + @mod_name 模块名称 + @fun_name 函数名 + """ + import datetime + data = {} + path = '{}/data/mod_log.json'.format(get_panel_path()) + if os.path.exists(path): + try: + data = json.loads(readFile(path)) + except: + pass + + if type(data) != dict: + data = {} + + key = datetime.datetime.now().strftime("%Y-%m-%d") + if not key in data: data[key] = {} + + if not mod_name in data[key]: + data[key][mod_name] = {} + + if not fun_name in data[key][mod_name]: + data[key][mod_name][fun_name] = 0 + + data[key][mod_name][fun_name] += count + + writeFile(path, json.dumps(data)) + return True + + + +headers_filter_rules = None +def filter_headers(): + ''' + @name 过滤请求头 + @author hwliang<2021-12-18> + @return dict + ''' + global headers_filter_rules + + # 预编译过滤规则 + if not headers_filter_rules: + headers_filter_rules = { + 'host': re.compile(r'^[\w\.\-\:]+$'), + 'accept': re.compile(r'^[\w\s\.\-\*\/\,\=\;\+]+$'), + 'accept-encoding': re.compile(r'^[\w\s\.\-\*\/\,]+$'), + 'accept-language': re.compile(r'^[\w\s\.\-\*\/\,\=\:\;]+$'), + 'cache-control': re.compile(r'^[\w\s\.\-\=\;]+$'), + 'connection': re.compile(r'^[\w\s\.\-]+$'), + 'content-length': re.compile(r'^[\d]+$'), + 'cookie': re.compile(r'^[\w\s\=\%\+\&\;\:\@\$\,\.\-\_\*\/\?\!\~\#]+$'), + 'origin': re.compile(r'^(http|https)://[\w\.\-\?\=\&\/\:]+$'), + 'pragma': re.compile(r'^[\w\s\.\-]+$'), + 'referer': re.compile(r'^(http|https)://[\w\.\-\?\=\&\/\:\%\#\~\!\*\+\@]+$'), + 'user-agent': re.compile(r'^[\w\s\.\-\*\/\,\(\)\=\+\;\:\@\$\,\.\-\_\~\#]+$'), + 'x-cookie-token': re.compile(r'^\w+$'), + 'x-http-token': re.compile(r'^\w+$'), + 'X-KL-Ajax-Request': re.compile(r'^\w+$'), + 'X-Requested-With': re.compile(r'^\w+$') + } + from flask import request + headers = request.headers + skeys = headers_filter_rules.keys() + + for k in skeys: + v = headers.get(k,None) + if not v: continue + if not headers_filter_rules[k].match(v): + return False + return True + +def trim(data): + """ + @去除所有空格 + """ + return data.replace(' ','').strip() + + +def get_os(_os='windows'): + """ + @验证系统版本 + """ + src_os = 'windows' + if os.path.exists('/www/server/panel'): src_os = 'linux' + if src_os == _os: return True + return False + + +def get_file_list(path, flist): + """ + 递归获取目录所有文件列表 + @path 目录路径 + @flist 返回文件列表 + """ + if os.path.exists(path): + files = os.listdir(path) + flist.append(path) + for file in files: + if os.path.isdir(path + '/' + file): + get_file_list(path + '/' + file, flist) + else: + flist.append(path + '/' + file) + + +def writeFile2(filename, s_body, mode='w+'): + """ + 写入字节文件内容 + @filename 文件名 + @s_body 欲写入的内容 + + """ + try: + fp = open(filename, mode); + fp.write(s_body) + fp.close() + return True + except: + return False + + +def check_obj_upgrade(_obj,filename = None): + ''' + @name 检查指定模块是否修改 + @author hwliang + @param 文件名 + @param 模块对象 + @return void + ''' + + # 引用缓存 + try: + from BTPanel import cache + except: + return + + # 是否传递文件名? + if not filename: + filename = _obj.__file__ + + # 获取文件修改时间 + skey = "obj_up_{}".format(md5(filename)) + mtime = os.path.getmtime(filename) # 当前 + old_mtime = cache.get(skey) # 旧的 + + # 直接设置当前修改时间 + if not old_mtime: + cache.set(skey,mtime) + return + + # 检查是否修改 + if old_mtime == mtime: + return + + # 重新加载模块 + import importlib + importlib.reload(_obj) + cache.set(skey,mtime) + + +def version_to_tuple(version): + ''' + @name 将版本号转为元组 + @version 字符串版本号 + @return 元组版本号 + ''' + if not version: + return () + if not isinstance(version, str): + return version + version = re.sub(r"[^\.\d]+","",version) + version = version.split('.') + version = tuple(map(int, version)) + return version + + +def set_search_history(mod_name,key,val): + """ + @保存搜索历史 + @mod_name 模块名称 + @key 关键字 + @val string 搜索内容 + """ + if not val: return False + + max = 10 + p_file = get_panel_path() + m_file = p_file + '/data/search.limit' + d_file = p_file + '/data/search.json' + try: + sdata = int(readFile(m_file)) + if sdata: max = sdata + except: pass + + result = {} + try: + result = json.loads(readFile(d_file)) + except :pass + + if not mod_name in result: result[mod_name] = {} + if not key in result[mod_name]: result[mod_name][key] = [] + + n_list = [] + for item in result[mod_name][key]: + if item['val'].strip() != val.strip(): n_list.append(item) + + n_list.append({'val':val,'time':int(time.time())}) + result[mod_name][key] = n_list[0:max] + + writeFile(d_file,json.dumps(result)) + return True + +def get_search_history(mod_name,key): + """ + @获取搜索历史 + @mod_name string 模块名称 + @key string 关键字 + """ + print(mod_name,key) + result = [] + d_file = get_panel_path() + '/data/search.json' + try: + result = json.loads(readFile(d_file))[mod_name][key] + except :pass + + result = sorted(result, key=lambda x: x['time'], reverse=True) + + return result + + +def set_dir_history(mod_name,key,val): + """ + @设置目录打开历史 + @mod_name string 模块名称 + @key string 函数名 + @val string 路径 + """ + if not val: return False + + max = 10 + result = {} + d_file = get_panel_path() + '/data/dir_history.json' + try: + result = json.loads(readFile(d_file)) + except :pass + + if not mod_name in result: result[mod_name] = {} + if not key in result[mod_name]: result[mod_name][key] = [] + + data = result[mod_name][key] + for info in data: + if val.find(info['val']) >= 0: + if time.time() - info['time'] < 15: + data.remove(info) + + data.append({'val':val,'time':int(time.time())}) + result[mod_name][key] = data[0:max] + writeFile(d_file,json.dumps(result)) + return True + + +def get_dir_history(mod_name,key): + """ + @获取目录打开历史 + @mod_name string 模块名称 + @key string 关键字 + """ + result = [] + d_file = get_panel_path() + '/data/dir_history.json' + try: + result = json.loads(readFile(d_file))[mod_name][key] + except :pass + return result + +def get_run_pip(): + pass + +def install_pip(shell): + """ + @name 安装pip模块 + @author cjxin + @param shell 安装命令 + """ + if get_os('windows'): + os.system(get_run_pip(shell.replace('pip','[PIP]'))) + else: + os.system(shell.replace('pip','btpip')) + + +def is_domain(domain): + """ + @验证是否域名 + """ + reg = "^([\w\-\*]{1,100}\.){1,10}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$"; + if re.match(reg, domain): return True + return False + +def init_msg(module): + """ + 初始化消息通道 + @module 消息通道模块名称 + """ + import os, sys + if not os.path.exists('class/msg'): os.makedirs('class/msg') + panelPath = get_panel_path() + + sfile = 'class/msg/{}_msg.py'.format(module) + if not os.path.exists(sfile): return False + sys.path.insert(0, "{}/class/msg".format(panelPath)) + + msg_main = __import__('{}_msg'.format(module)) + try: + mod_reload(msg_main) + except: + pass + return eval('msg_main.{}_msg()'.format(module)) + + +def push_argv(msg): + """ + @处理短信参数,否则会被拦截 + """ + if is_ipv4(msg): + tmp1 = msg.split('.') + msg = '{}.***.***.{}'.format(tmp1[0], tmp1[3]) + else: + if is_domain(msg): + msg = msg.replace('.', '_') + return msg + + +def check_sms_argv(data): + """ + @批量处理短信参数,否则会被拦截 + """ + for key in data: + val = data[key] + if type(val) == str: + data[key] = push_argv(val) + return data + +""" +@获取推送ip +""" +def get_push_address(): + ip = push_argv(GetLocalIp()) + return ip + + +# def get_ips_area(ips): +# ''' +# @name 获取ip地址所在地 +# @author cjxin +# @param ips +# @return list +# ''' +# +# run_object = getattr(def_object.main(),def_name,None) +# args = dict_obj() +# args.model_index = 'safe' +# args.ips = ips +# +# res = PluginLoader.module_run("ips","get_ip_area",args) +# return res + +# def return_area(result,key): +# """ +# @name 格式化返回带IP归属地的数组 +# @param result 数据数组 +# @param key ip所在字段 +# @return list +# """ +# tmps = [] +# for data in result: +# data['area'] = '' +# tmps.append(data[key]) +# +# res = get_ips_area(tmps) +# if 'status' in res: return result +# +# for data in result: +# if data[key] in res: +# data['area'] = res[data[key]] +# return result + +def get_network_ip(): + """ + @name 获取本机ip + @return string + """ + + import socket + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(('8.8.8.8', 80)) + ip = s.getsockname()[0] + return ip + finally: + s.close() + return '127.0.0.1' + +def get_server_ip(): + """ + @获取服务器外网ip + """ + + user_file = '{}/data/userInfo.json'.format(get_panel_path()) + if os.path.exists(user_file): + try: + userTmp = json.loads(readFile(user_file)) + return userTmp['address'] + except: pass + + return GetLocalIp() + + +def get_push_info(title,slist = []): + """ + @name 获取推送信息 + @param title 推送标题 + @param slist 推送追加的列表 + 如:slist = ['>发送内容:xxx'] + @return dict + """ + data = {} + data['title'] = title + data['ip'] = get_server_ip() + + data['local_ip'] = get_network_ip() + data['time'] = format_date() + data['server_name'] = GetConfigValue('title') + + dlist = [ + "#### {}".format(data['title']), + ">Server:"+ data['server_name'] , + ">IP:{}(Extranet) {}(Intranet)".format(data['ip'],data['local_ip']) , + ">Send:" + data['time'] + ] + dlist.extend(slist) + msg = "\n\n".join(dlist) + data['msg'] = msg + data['list'] = dlist + return data + +def write_push_log(module,msg,res): + """ + @name 写推送日志 + @module string 模块名称 + @msg string 消息内容 + @res dict 推送结果 + """ + user = '' + for key in res: + status = '成功' + if res[key] == 0: status = '失败' + user += '[ {}:{} ] '.format(key,status) + + if not user: user = '[ Default ] ' + try: + msg_obj = init_msg(module) + if msg_obj: module = msg_obj.get_version_info(None)['title'] + except:pass + + log = '[{}] send to {}, content:[{}]'.format(module,user,xsssec(msg)) + WriteLog('Message Push',log) + return True + +def push_msg(module,data): + """ + @name 推送消息 + @param module 模块名称 + @param msg 消息内容 + @return dict + """ + msg_obj = init_msg(module) + if not msg_obj: + returnMsg(False,'模块{}不存在!'.format(module)) + + res = msg_obj.push_data(data) + return res + +def check_chinese(data): + """ + @name 判断字符串是否包含中文 + """ + if re.search('[\u4e00-\u9fa5]',data): + return True + return False + +def is_ssl(): + ''' + @name 是否开启SSL + @author hwliang + @return bool + ''' + return os.path.exists(get_panel_path() + '/data/ssl.pl') + +def get_cookie(key,default=None): + ''' + @name 获取指定Cookie值 + @author hwliang + @param key Cookie键 + @param default 默认值 + @return str + ''' + from flask import request + return request.cookies.get(key,default) + +def get_csrf_cookie_token_key(): + ''' + @name 获取CSRF Cookie Key + @author hwliang + @return string + ''' + if is_ssl(): + token_key = 'request_token' + else: + token_key = 'request_token' + return token_key + + +def get_csrf_cookie_token_value(): + ''' + @name 获取CSRF Cookie Value + @author hwliang + @return string + ''' + token_key = get_csrf_cookie_token_key() + return get_cookie(token_key) + + +def get_csrf_html_token_key(): + ''' + @name 获取CSRF HTML Key + @author hwliang + @return string + ''' + if is_ssl(): + token_key = 'request_token_head' + else: + token_key = 'request_token_head' + return token_key + +def get_csrf_html_token_value(): + ''' + @name 获取CSRF HTML Value + @author hwliang + @return string + ''' + token_key = get_csrf_html_token_key() + return get_cookie(token_key) + +def get_csrf_sess_html_token_value(): + ''' + @name 从SESSION获取CSRF HTML value + @author hwliang + @return string + ''' + from flask import session + return session.get(get_csrf_html_token_key(),"") + +def get_csrf_sess_cookie_token_value(): + ''' + @name 从SESSION获取CSRF Cookie value + @author hwliang + @return string + ''' + from flask import session + return session.get(get_csrf_cookie_token_key(),"") + +def get_sys_install_bin(): + ''' + @name 获取系统包管理器命令 + @author hwliang + @return string + ''' + install_bins = ['/usr/bin/yum','/usr/bin/apt-get','/usr/bin/dnf'] + for bin in install_bins: + if os.path.exists(bin): + return bin + return '' + + + +def get_firewall_status(): + ''' + @name 获取系统防火墙状态 + @author hwliang + @return int 0.关闭 1.开启 -1.未安装 + ''' + import psutil + firewall_files = {'/usr/sbin/firewalld':"pid",'/usr/bin/firewalld':"pid",'/usr/sbin/ufw':"/usr/sbin/ufw status|grep -E '(Status: active|激活)'",'/sbin/ufw':"/sbin/ufw status |grep -E '(Status: active|激活)'",'/usr/sbin/iptables':"service iptables status|grep 'Chain INPUT'"} + for f in firewall_files.keys(): + if not os.path.exists(f): continue + _cmd = firewall_files[f] + if _cmd != "pid": + res = ExecShell(_cmd) + if res[0].strip(): + return 1 + else: + return 0 + for pid in psutil.pids(): + try: + p = psutil.Process(pid) + if f in p.cmdline(): + return 1 + except: pass + return 0 + return -1 + +def get_panel_port(): + ''' + @name 获取面板端口 + @author hwliang + @return int + ''' + port_file = '{}/data/port.pl'.format(get_panel_path()) + if not os.path.exists(port_file): + return 8888 + try: + return int(readFile(port_file)) + except: + return 8888 + +def install_sys_firewall(): + ''' + @name 安装系统防火墙 + @author hwliang + @return bool + ''' + if get_firewall_status() != -1: return True + + install_bin = get_sys_install_bin() + if not install_bin: return False + if install_bin.find('apt-get') != -1: + ExecShell("{} install -y ufw".format(install_bin)) + if get_firewall_status() != -1: + _cmd = '''ufw allow 20/tcp +ufw allow 21/tcp +ufw allow 22/tcp +ufw allow 80/tcp +ufw allow 443/tcp +ufw allow ${panelPort}/tcp +ufw allow ${sshPort}/tcp +ufw allow 39000:40000/tcp +ufw_status=`ufw status` +echo y|ufw enable +ufw default deny +ufw reload +'''.format(panelPort=get_panel_port(),sshPort=get_ssh_port()) + ExecShell(_cmd) + elif install_bin.find('yum') != -1 or install_bin.find('dnf') != -1: + ExecShell("{} install -y firewalld".format(install_bin)) + if get_firewall_status() != -1: + _cmd = '''systemctl enable firewalld + systemctl start firewalld + firewall-cmd --set-default-zone=public > /dev/null 2>&1 + firewall-cmd --permanent --zone=public --add-port=20/tcp > /dev/null 2>&1 + firewall-cmd --permanent --zone=public --add-port=21/tcp > /dev/null 2>&1 + firewall-cmd --permanent --zone=public --add-port=22/tcp > /dev/null 2>&1 + firewall-cmd --permanent --zone=public --add-port=80/tcp > /dev/null 2>&1 + firewall-cmd --permanent --zone=public --add-port=443/tcp > /dev/null 2>&1 + firewall-cmd --permanent --zone=public --add-port={panelPort}/tcp > /dev/null 2>&1 + firewall-cmd --permanent --zone=public --add-port={sshPort}/tcp > /dev/null 2>&1 + firewall-cmd --permanent --zone=public --add-port=39000-40000/tcp > /dev/null 2>&1 + firewall-cmd --reload +'''.format(panelPort = get_panel_port(),sshPort = get_ssh_port()) + ExecShell(_cmd) + + return False + + +# def check_firewall_rule(port): +# ''' +# @name 检测防火墙是否已经添加规则 +# @author cjxin +# @param port int 端口号 +# ''' +# +# args = dict_obj() +# args.model_index = 'safe' +# args.port = port +# +# import PluginLoader +# res = PluginLoader.module_run("firewall","check_firewall_rule",args) +# return res + +# def add_firewall_rule(port,protocol = 'tcp',types = 'accept',address = '0.0.0.0/0',brief = None): +# """ +# @name 添加防火墙规则 +# @author cjxin +# @param port int 端口号 +# @param protocol string 协议类型 tcp udp +# @param types string 添加类型 accept reject +# @param address string 地址 +# @param brief string 描述 +# """ +# +# args = dict_obj() +# args.model_index = 'safe' +# args.port = port +# args.protocol = protocol +# args.types = types +# args.address = address +# args.brief = brief +# if not brief: args.brief = str(port) +# +# import PluginLoader +# res = PluginLoader.module_run("firewall","create_rules",args) +# return res + +def del_firewall_rule(port,protocol = 'tcp',types = 'accept',address = '0.0.0.0/0'): + ''' + @name 删除防火墙规则 + @author cjxin + @param port int 端口号 + @param protocol str 协议 + @param types str 类型 + @param address str 地址 + ''' + + + args = dict_obj() + args.model_index = 'safe' + args.port = port + args.protocol = protocol + args.types = types + args.address = address + from pluginAuth import Plugin + plu = Plugin("firewall") + res = plu.get_fun("remove_rules")(args) + return res + + +def is_aarch(): + ''' + @name 是否是arm架构 + @author hwliang + @return bool + ''' + uname = None + if hasattr(os,'uname'): uname = os.uname() + aarch_list = ['aarch64','aarch'] + try: + return uname.machine in aarch_list + except: + if uname: + return uname[-1] in aarch_list + return False + +def is_process_exists_by_cmdline(_cmd): + ''' + @name 根据命令行参数查找进程是否存在 + @author hwliang + @param _cmd 命令行 + @return bool + ''' + if isinstance(_cmd,str): + _cmd = [_cmd] + if not isinstance(_cmd,list): + return False + for pid in psutil.pids(): + try: + p = psutil.Process(pid) + cmd_line = p.cmdline() + for _c in _cmd: + if _c in cmd_line: + return True + except: + continue + return False + +def is_process_exists_by_exe(_exe): + ''' + @name 根据执行文件路径查找进程是否存在 + @author hwliang + @param _exe 命令行 + @return bool + ''' + if isinstance(_exe,str): + _exe = [_exe] + if not isinstance(_exe,list): + return False + for pid in psutil.pids(): + try: + p = psutil.Process(pid) + _exe_bin = p.exe() + for _e in _exe: + if _exe_bin.find(_e) != -1: return True + except: + continue + return False + +def is_process_exists_by_name(_name): + ''' + @name 根据进程名查找进程是否存在 + @author hwliang + @param _name 命令行 + @return bool + ''' + if isinstance(_name,str): + _name = [_name] + if not isinstance(_name,list): + return False + for pid in psutil.pids(): + try: + p = psutil.Process(pid) + name = p.name() + for _n in _name: + if name == _n: return True + except: + continue + return False + + +def is_mysql_process_exists(): + ''' + @name 检查mysql进程是否存在 + @author hwliang + @return bool + ''' + _exe = ['server/mysql/bin/mysqld_safe','server/mysql/bin/mariadbd','server/mysql/bin/mysqld'] + return is_process_exists_by_exe(_exe) + +def is_redis_process_exists(): + ''' + @name 检查redis进程是否存在 + @author hwliang + @return bool + ''' + _exe = ['server/redis/src/redis-server'] + return is_process_exists_by_exe(_exe) + +def is_pure_ftpd_process_exists(): + ''' + @name 检查pure-ftpd进程是否存在 + @author hwliang + @return bool + ''' + _exe = ['server/pure-ftpd/sbin/pure-ftpd'] + return is_process_exists_by_exe(_exe) + +def is_php_fpm_process_exists(name): + ''' + @name 检查php-fpm进程是否存在 + @author hwliang + @return bool + ''' + _php_version = name.split('-')[-1] + _exe = ['server/php/{}/sbin/php-fpm'.format(_php_version)] + return is_process_exists_by_exe(_exe) + +def is_nginx_process_exists(): + ''' + @name 检查nginx进程是否存在 + @author hwliang + @return bool + ''' + _exe = ['server/nginx/sbin/nginx'] + return is_process_exists_by_exe(_exe) + +def is_httpd_process_exists(): + ''' + @name 检查httpd进程是否存在 + @author hwliang + @return bool + ''' + _exe = ['server/apache/bin/httpd'] + return is_process_exists_by_exe(_exe) + +def is_memcached_process_exists(): + ''' + @name 检查memcached进程是否存在 + @author hwliang + @return bool + ''' + _exe = ['/usr/local/memcached/bin/memcached'] + return is_process_exists_by_exe(_exe) + +def is_mongodb_process_exists(): + ''' + @name 检查mongodb进程是否存在 + @author hwliang + @return bool + ''' + _exe = ['server/mongodb/bin/mongod'] + return is_process_exists_by_exe(_exe) + + +def get_admin_path(): + ''' + @name 取安全入口 + @author hwliang + @return string + ''' + login_path = '/login' + path = '{}/data/admin_path.pl'.format(get_panel_path()) + if not os.path.exists(path): return login_path + admin_path = readFile(path) + if not admin_path: return login_path + admin_path = admin_path.strip() + if admin_path in ['','/']: + return login_path + return admin_path + + +def get_improvement(): + ''' + @name 获取用户体验改进计划状态 + @author hwliang + @return bool + ''' + tip_file = '{}/data/improvement.pl'.format(get_panel_path()) + tip_file_set = '{}/data/is_set_improvement.pl'.format(get_panel_path()) + if not os.path.exists(tip_file_set): + return True + return os.path.exists(tip_file) + + +def is_spider(): + ''' + @name 判断是否为爬虫 + @return bool + ''' + from BTPanel import request + import panelDefense + p = panelDefense.bot_safe() + return not p.spider(request.headers.get('User-Agent'),request.remote_addr) + + diff --git a/class/safe_warning/sw_mysql_port.py b/class/safe_warning/sw_mysql_port.py index 822f14a5..87374725 100644 --- a/class/safe_warning/sw_mysql_port.py +++ b/class/safe_warning/sw_mysql_port.py @@ -51,8 +51,23 @@ def check_run(): if not public.ExecShell("lsof -i :{}".format(port_tmp[0]))[0]: return True,'MySQL is not installed' result = public.check_port_stat(int(port_tmp[0]),public.GetLocalIp()) - if result == 0: - return True,'Risk-free' + #兼容socket能连通但实际端口不通情况 + if result != 0: + res='' + if os.path.exists('/usr/sbin/firewalld'): + res=public.ExecShell('firewall-cmd --list-all') + elif os.path.exists('/usr/sbin/ufw'): + try: + res=public.ExecShell('sudo ufw status verbose') + except: + res=public.ExecShell('ufw status verbose') + else: + pass + check_str=' '+port_tmp[0]+'/' + if res[0].find(check_str) == -1: + return True,'Risk-free' + else:return True,'Risk-free' + fail2ban_file = '/www/server/panel/plugin/fail2ban/config.json' if os.path.exists(fail2ban_file): diff --git a/class/safe_warning/sw_panel_port.py b/class/safe_warning/sw_panel_port.py index 4824286e..b98ff225 100644 --- a/class/safe_warning/sw_panel_port.py +++ b/class/safe_warning/sw_panel_port.py @@ -37,7 +37,7 @@ def check_run(): port = public.readFile(port_file) if not port: return True,'Rick-free' port = int(port) - if port != 8888 and port != 7800: + if port != 8888: return True,'Rick-free' return False,'The panel port is the default port ({}), which may cause unnecessary security risks'.format(port) diff --git a/class/safe_warning/sw_site_spath.py b/class/safe_warning/sw_site_spath.py index 21dc59d3..5f2e24d1 100644 --- a/class/safe_warning/sw_site_spath.py +++ b/class/safe_warning/sw_site_spath.py @@ -35,7 +35,7 @@ def check_run(): @return tuple (status,msg) ''' not_uini = [] - site_list = public.M('sites').where('status=?',(1,)).field('name,path').select() + site_list = public.M('sites').where('status=? AND project_type=?',(1,'PHP')).field('name,path').select() for s in site_list: path = get_site_run_path(s['name'],s['path']) user_ini = path + '/.user.ini' diff --git a/class/send_to_user.py b/class/send_to_user.py index bed6f87f..41b5c7c5 100644 --- a/class/send_to_user.py +++ b/class/send_to_user.py @@ -109,7 +109,6 @@ class send_to_user: def read_thread(self): if not public.M('send_settings').count():return False send_data=public.M('send_settings').field('id,name,type,path,send_type,inser_time,last_time,time_frame').select() - print(send_data) for i in send_data: if (int(time.time())-int(i['last_time']))'+i2[1]+',触发告警时间:'+self.dtchg(int(time.time())),'json',i2[0]) + self.inser_send_msg(i['name'],i['send_type'],self.get_ip()+"服务器触发告警信息为: "+i2[1]+',触发告警时间:'+self.dtchg(int(time.time())),'json',i2[0]) public.writeFile(i['path'], '') public.M('send_settings').where("id=?", (i['id'],)).update({"last_time": int(time.time())}) - continue if i['type']=='file': if os.path.exists(i['path']): - self.inser_send_msg(i['name'], i['send_type'], '堡塔'+i['name']+'提醒您服务器'+self.get_ip()+'存在异常,详情请登陆面板查看'+i['name']+',触发告警时间:'+self.dtchg(int(time.time())), 'file', int(time.time())) - public.M('send_settings').where("id=?", (i['id'],)).update({"last_time": int(time.time())}) - os.system('rm -rf %s'%i['path']) - if os.path.exists(i['path']):os.system('rm -rf %s'%i['path']) - else: - continue - def send(self,title,body): - tongdao = self.mail.get_settings() - return self.mail.qq_smtp_send(tongdao['user_mail']['mail_list'], title=title, body=body) - def send_dingding(self,count): - return self.mail.dingding_send(count) + try: + f=open(i['path'],'r') + for i2 in f: + i2 =i2.strip() + if i2: + self.inser_send_msg(i['name'], i['send_type'],self.get_ip()+"服务器触发告警信息为: "+i2+',触发告警时间:'+self.dtchg(int(time.time())), 'file', int(time.time())) + os.remove(i['path']) + public.M('send_settings').where("id=?", (i['id'],)).update({"last_time": int(time.time())}) + except: + os.remove(i['path']) def __write_log(self,name, msg): public.WriteLog(name+'告警', msg) @@ -146,34 +143,17 @@ class send_to_user: count=1 for i in send_msg: if count>=4:break - settings=self.mail.get_settings() if i['send_type']=='mail': - if not settings['user_mail']['user_name']:continue - if i['name']=='Nginx防火墙' or i['name'] == 'Apache防火墙': - if self.send(i['name'] + '提醒您' + self.get_ip() + '服务器正在遭受攻击', i['msg']): - self.__write_log(i['name'], i['msg']) - public.M('send_msg').where("id=?", (i['id'],)).update({"is_send": True}) - else: - if self.send(i['name']+'提醒您'+self.get_ip()+'服务器存在风险', i['msg']): - self.__write_log(i['name'],i['msg']) - public.M('send_msg').where("id=?", (i['id'],)).update({"is_send":True}) + if public.send_mail(i['name'], i['msg']): + self.__write_log(i['name'], i['msg']) + public.M('send_msg').where("id=?", (i['id'],)).update({"is_send":True}) if i['send_type']=='dingding': - if not settings['dingding']['dingding']: continue - if i['name'] == 'Nginx防火墙' or i['name'] == 'Apache防火墙': - if self.send(i['name'] + '提醒您' + self.get_ip() + '服务器正在遭受攻击', i['msg']): - self.__write_log(i['name'], i['msg']) - public.M('send_msg').where("id=?", (i['id'],)).update({"is_send": True}) - else: - if self.send_dingding(i['msg']): - self.__write_log(i['name'], i['msg']) - public.M('send_msg').where("id=?", (i['id'],)).update({"is_send": True}) + if public.send_dingding(i['msg']): + self.__write_log(i['name'], i['msg']) + public.M('send_msg').where("id=?", (i['id'],)).update({"is_send": True}) count += 1 public.M('send_msg').where("is_send=?", (True,)).delete() def main(self): - try: - self.read_thread() - self.send_msg() - except: - pass - \ No newline at end of file + self.read_thread() + self.send_msg() \ No newline at end of file diff --git a/class/setPanelLets.py b/class/setPanelLets.py index e5418180..e5a018e6 100644 --- a/class/setPanelLets.py +++ b/class/setPanelLets.py @@ -93,6 +93,7 @@ class setPanelLets: now = time.time() if time_stamp > int(now): return i + for i in gcl: for d in i['dns']: d = d.split('.') if '*' in d and d[1:] == get.domain.split('.')[1:]: @@ -139,8 +140,8 @@ class setPanelLets: public.writeFile(self.__panel_cert_path + "certificate.pem", self.__tmp_cert) # 记录证书源 - def __save_cert_source(self,domain,email): - public.writeFile(self.__panel_cert_path+"lets.info",json.dumps({"domain":domain,"cert_type":"2","email":email})) + def __save_cert_source(self,domain): + public.writeFile(self.__panel_cert_path+"lets.info",json.dumps({"domain":domain,"cert_type":"2"})) # 获取证书源 def get_cert_source(self): @@ -192,10 +193,10 @@ class setPanelLets: panel_cert_data = self.__check_panel_cert() if not panel_cert_data: self.__write_panel_cert() - return public.returnMsg(True,'') + return public.returnMsg(True,'1') if panel_cert_data["key"] != self.__tmp_key and panel_cert_data["cert"] != self.__tmp_cert: self.__write_panel_cert() - return public.returnMsg(True,'') + return public.returnMsg(True,'1') return public.returnMsg(True, '') # 设置lets证书 @@ -209,7 +210,7 @@ class setPanelLets: domain = self.__check_panel_domain() get.domain = domain if not domain: - return public.returnMsg(False, "You need to bind the domain name to the panel before you can apply for the Let\'s Encrypt certificate.") + return public.returnMsg(False, "You need to bind the domain name to the panel before you can apply for the Lets Encrypt certificate.") if not self.__check_host_name(domain): create_site = self.__create_site_of_panel_lets(get) domain_cert = self.__check_cert_dir(get) @@ -218,9 +219,9 @@ class setPanelLets: if not res['status']: return res public.writeFile("/www/server/panel/data/ssl.pl", "True") - public.writeFile("/www/server/panel/data/reload.pl","1") - self.__save_cert_source(domain,get.email) - return public.returnMsg(True, 'Panel lets set successfully') + # public.writeFile("/www/server/panel/data/reload.pl","1") + self.__save_cert_source(domain) + return public.returnMsg(True, 'Setup successfully!') if not create_site: create_lets = self.__create_lets(get) if not create_lets['status']: @@ -229,9 +230,9 @@ class setPanelLets: domain_cert = self.__check_cert_dir(get) self.copy_cert(domain_cert) public.writeFile("/www/server/panel/data/ssl.pl", "True") - public.writeFile("/www/server/panel/data/reload.pl", "1") - self.__save_cert_source(domain, get.email) - return public.returnMsg(True, 'Panel lets set successfully') + # public.writeFile("/www/server/panel/data/reload.pl", "1") + self.__save_cert_source(domain) + return public.returnMsg(True, 'Setup successfully!') else: return public.returnMsg(False, create_lets) else: diff --git a/class/sewer/dns_providers/cloudflare.py b/class/sewer/dns_providers/cloudflare.py index 983aac29..d7235949 100644 --- a/class/sewer/dns_providers/cloudflare.py +++ b/class/sewer/dns_providers/cloudflare.py @@ -42,7 +42,7 @@ class CloudFlareDns(common.BaseDns): ) if find_dns_zone_response.status_code != 200: raise ValueError( - "Error creating cloudflare dns record: status_code={status_code} response={response}".format( + "Error creating cloudflare model record: status_code={status_code} response={response}".format( status_code=find_dns_zone_response.status_code, response=self.log_response(find_dns_zone_response), ) @@ -92,7 +92,7 @@ class CloudFlareDns(common.BaseDns): # raise error so that we do not continue to make calls to ACME # server raise ValueError( - "Error creating cloudflare dns record: status_code={status_code} response={response}".format( + "Error creating cloudflare model record: status_code={status_code} response={response}".format( status_code=create_cloudflare_dns_record_response.status_code, response=self.log_response(create_cloudflare_dns_record_response), ) diff --git a/class/sewer_Usage.py b/class/sewer_Usage.py index df4459b7..f2f9e63e 100644 --- a/class/sewer_Usage.py +++ b/class/sewer_Usage.py @@ -44,12 +44,12 @@ import hmac try: import requests except: - os.system('pip install requests') + os.system('btpip install requests') import requests try: import OpenSSL except: - os.system('pip install pyopenssl') + os.system('btpip install pyOpenSSL') import OpenSSL import random import datetime @@ -252,7 +252,7 @@ class ACMEclient(object): print("Apply for a certificate") identifiers = [] for domain_name in self.all_domain_names: - identifiers.append({"type": "model", "value": domain_name}) + identifiers.append({"type": "dns", "value": domain_name}) payload = {"identifiers": identifiers} url = self.ACME_NEW_ORDER_URL apply_for_cert_issuance_response = self.make_signed_acme_request(url=url, payload=payload) @@ -299,7 +299,7 @@ class ACMEclient(object): if wildcard: domain = "*." + domain for i in res["challenges"]: - if i["type"] == "model-01": + if i["type"] == "dns-01": dns_challenge = i dns_token = dns_challenge["token"] dns_challenge_url = dns_challenge["url"] @@ -806,7 +806,7 @@ class AliyunDns(object): msg = public.GetMsg("CANT_FIND_RECORDID"), domain_name print(msg) return - print("start to delete model record, id: ", record_id) + print("start to delete dns record, id: ", record_id) randomint = random.randint(11111111111111, 99999999999999) now = datetime.datetime.utcnow() otherStyleTime = now.strftime("%Y-%m-%dT%H:%M:%SZ") @@ -901,7 +901,7 @@ class Dns_com(object): def create_dns_record(self, domain_name, domain_dns_value): root, _, acme_txt = extract_zone(domain_name) print("create_dns_record,", acme_txt, domain_dns_value) - result = public.ExecShell('''{} /www/server/panel/plugin/model/dns_main.py add_txt {} {}'''.format(public.get_python_bin(),acme_txt + '.' + root, domain_dns_value)) + result = public.ExecShell('''{} /www/server/panel/plugin/dns/dns_main.py add_txt {} {}'''.format(public.get_python_bin(),acme_txt + '.' + root, domain_dns_value)) if result[0].strip() == "False": sys.exit(json.dumps({"data": public.GetMsg("BT_DNSRES_ERR")})) print("create_dns_record_end") @@ -909,7 +909,7 @@ class Dns_com(object): def delete_dns_record(self, domain_name, domain_dns_value): root, _, acme_txt = extract_zone(domain_name) print("delete_dns_record start: ", acme_txt, domain_dns_value) - public.ExecShell('''{} /www/server/panel/plugin/model/dns_main.py remove_txt {} {}'''.format(public.get_python_bin() ,acme_txt + '.' + root, domain_dns_value)) + public.ExecShell('''{} /www/server/panel/plugin/dns/dns_main.py remove_txt {} {}'''.format(public.get_python_bin() ,acme_txt + '.' + root, domain_dns_value)) print("delete_dns_record_success") @@ -939,9 +939,9 @@ if __name__ == "__main__":#dns调用验证脚本 dns_class = AliyunDns(key=key, secret=secret) elif dnsapi == "dns_cx": # CloudXns dns_class = CloudxnsDns(key=key, secret=secret) - elif dnsapi == "dns_bt": # model.com + elif dnsapi == "dns_bt": # dns.com dns_class = Dns_com() - elif dnsapi == "model": # 手动的 + elif dnsapi == "dns": # 手动的 dns_class = Dns_Manual() Manual = 1 domain_alt_names = data['domain_alt_names'].split(",") diff --git a/class/site_dir_auth.py b/class/site_dir_auth.py index 5d0bf2ef..73ffd423 100644 --- a/class/site_dir_auth.py +++ b/class/site_dir_auth.py @@ -62,31 +62,32 @@ class SiteDirAuth: :param get: :return: ''' - if len(get.username) < 3 or len(get.password) < 3: - return public.returnMsg(False, 'The account number or password cannot be less than 3 characters') - name = get.name + # if len(get.username) < 3 or len(get.password) < 3: + # return public.return_msg_gettext(False, 'Username or password cannot be less than 3 characters') + # name = get.name + param = self.__check_param(get) + if not param['status']: + return param + param = param['msg'] + password = param['password'] + username = param['username'] + name = param['name'] site_dir = get.site_dir if public.get_webserver() == "openlitespeed": - return public.returnMsg(False,"OpenLiteSpeed is currently not supported") - if not hasattr(get,"password") or not get.password or not hasattr(get,"username") or not get.username: - return public.returnMsg(False, 'Please enter an account or password') + return public.return_msg_gettext(False,"OpenLiteSpeed is currently not supported") + # if not hasattr(get,"password") or not get.password or not hasattr(get,"username") or not get.username: + # return public.return_msg_gettext(False, 'Please enter an account or password') if not get.site_dir: - return public.returnMsg(False, 'Please enter the directory to be protected') + return public.return_msg_gettext(False, 'Please enter the directory to be protected') if not get.name: - return public.returnMsg(False, 'Please enter the Name') - - # 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) + return public.return_msg_gettext(False, 'Please enter the Name') + passwd = public.hasPwd(password) site_info = self.get_site_info(get.id) site_name = site_info["site_name"] if self._check_site_authorization(site_name): - return public.returnMsg(False, 'Site password protection has been set, please cancel and then set. Site directory --> Password access') + return public.return_msg_gettext(False, 'Site password protection has been set, please cancel and then set. Site directory --> Password access') if self._check_dir_auth(site_name, name,site_dir): - return public.returnMsg(False, 'Directory has been protected') + return public.return_msg_gettext(False, 'Directory has been protected') auth = "{user}:{passwd}".format(user=get.username,passwd=passwd) auth_file = '{setup_path}/pass/{site_name}'.format(setup_path=self.setup_path,site_name=site_name) if not os.path.exists(auth_file): @@ -108,7 +109,7 @@ class SiteDirAuth: conf = {"name":name,"site_dir":get.site_dir,"auth_file":auth_file} self._write_conf(conf,site_name) public.serviceReload() - return public.returnMsg(True,"Created successfully") + return public.return_msg_gettext(True,"Successfully created") # 检查配置是否存在 def _check_dir_auth(self, site_name, name,site_dir): @@ -126,6 +127,10 @@ class SiteDirAuth: conf = public.readFile(self.setup_path + '/panel/vhost/'+public.get_webserver()+'/'+siteName+'.conf'); if public.get_webserver() == 'nginx': rep = "enable-php-(\w{2,5})\.conf" + tmp = re.search(rep,conf) + if not tmp: + rep = "enable-php-(\d+-wpfastcgi).conf" + re.search(rep, conf) else: rep = "php-cgi-(\w{2,5})\.sock" tmp = re.search(rep,conf).groups() @@ -134,7 +139,7 @@ class SiteDirAuth: else: return "" except: - return public.returnMsg(False, 'SITE_PHPVERSION_ERR_A22') + return public.return_msg_gettext(False, 'Apache2.2 does NOT support MultiPHP!') # 获取站点名 def get_site_info(self,id): @@ -179,6 +184,8 @@ class SiteDirAuth: %s #AUTH_END }''' % (site_dir,auth_file,php_conf) + public.writeFile("/tmp/2", conf) + else: # 设置apache conf = ''' @@ -211,10 +218,12 @@ class SiteDirAuth: conf = public.readFile(file) if i == "apache": if act == "create": - rep = "combined(\n|.)+IncludeOptional.*\/dir_auth\/.*conf" - rep1 = "combined" - if not re.search(rep,conf): - conf = conf.replace(rep1, rep1 + "\n\t#Directory protection rules, do not manually delete\n\tIncludeOptional {}".format(dir_auth_file)) + rep = "IncludeOptional.*\/dir_auth\/.*conf(\n|.)+<\/VirtualHost>" + rep1 = "" + if not re.search(rep, conf): + conf = conf.replace(rep1, + "\n\t#Directory protection rules, do not manually delete\n\tIncludeOptional {}\n".format( + dir_auth_file)) else: rep = "\n*#Directory protection rules, do not manually delete\n+\s+IncludeOptional[\s\w\/\.\*]+" conf = re.sub(rep, '', conf) @@ -240,7 +249,7 @@ class SiteDirAuth: # for i in range(len(a_conf)-1,-1,-1): # if site_name == a_conf[i]["sitename"] and a_conf[i]["proxyname"]: # del a_conf[i] - return public.returnMsg(False, 'ERROR: %s
                                      ' % public.GetMsg("CONFIG_ERROR") + isError.replace("\n", + return public.return_msg_gettext(False, 'ERROR: %s
                                      ' % public.get_msg_gettext('Configuration ERROR') + isError.replace("\n", '
                                      ') + '
                                      ') # 删除密码保护 @@ -256,7 +265,7 @@ class SiteDirAuth: site_name = site_info["site_name"] conf = self._read_conf() if site_name not in conf: - return public.returnMsg(False,"The website does not exist in the configuration:{}".format(site_name)) + return public.return_msg_gettext(False,"The website does not exist in the configuration:{}",(site_name,)) for i in range(len(conf[site_name])): if name in conf[site_name][i].values(): print(conf[site_name][i]) @@ -275,7 +284,7 @@ class SiteDirAuth: self.set_conf(site_name,"delete") if not hasattr(get,'multiple'): public.serviceReload() - return public.returnMsg(True,"DEL_SUCCESS") + return public.return_msg_gettext(True,'Successfully deleted!') # 修改目录保护密码 def modify_dir_auth_pass(self,get): @@ -287,6 +296,14 @@ class SiteDirAuth: :param get: :return: ''' + # if not hasattr(get,"password") or not get.password or not hasattr(get,"username") or not get.username: + # return public.return_msg_gettext(False, 'Username or password cannot be less than 3 characters') + param = self.__check_param(get) + if not param['status']: + return param + param = param['msg'] + password = param['password'] + username = param['username'] name = get.name site_info = self.get_site_info(get.id) site_name = site_info["site_name"] @@ -295,7 +312,7 @@ class SiteDirAuth: auth_file = '{setup_path}/pass/{site_name}/{name}.pass'.format(setup_path=self.setup_path,site_name=site_name,name=name) public.writeFile(auth_file,auth) public.serviceReload() - return public.returnMsg(True,"EDIT_SUCCESS") + return public.return_msg_gettext(True,'Setup successfully!') # 获取目录保护列表 def get_dir_auth(self,get): @@ -314,3 +331,39 @@ class SiteDirAuth: if site_name in conf: return {site_name:conf[site_name]} return {} + + def __check_param(self, get): + values = {} + if hasattr(get, "password"): + if not get.password: + return public.returnMsg(False, 'Please enter password!') + password = get.password.strip() + if len(password) < 3: + return public.returnMsg(False, 'Password cannot be less than 3 characters') + if re.search('\s', password): + return public.returnMsg(False, 'Password cannot contain spaces') + values['password'] = password + + if hasattr(get, "username"): + if not get.username: + return public.returnMsg(False, 'Please enter username!') + username = get.username.strip() + if len(username) < 3: + return public.returnMsg(False, 'Username cannot be less than 3 characters') + if re.search('\s', username): + return public.returnMsg(False, 'Username cannot contain spaces') + values['username'] = username + + if hasattr(get, "name"): + if not get.name: + return public.returnMsg(False, 'Please enter a name!') + name = get.name.strip() + if len(name) < 3: + return public.returnMsg(False, 'Name cannot be less than 3 characters') + if re.search('\s', name): + return public.returnMsg(False, 'Name cannot contain spaces') + if re.search('[\/\"\'\!@#$%^&*()+={}\[\]\:\;\?><,./\\\]+', name): + return public.returnMsg(False, 'Name format must be [ aaa_bbb ]') + values['name'] = name + + return public.returnMsg(True, values) \ No newline at end of file diff --git a/class/sites.py b/class/sites.py index 1e3b3bf6..300c5600 100644 --- a/class/sites.py +++ b/class/sites.py @@ -200,7 +200,7 @@ server }], "proxy": { "open": False, - "url": "http://www.bt.cn", + "url": "https://www.bt.cn", "host": "www.bt.cn", "subOpen": False, "src": "", diff --git a/class/ssh_authentication.py b/class/ssh_authentication.py index 4c0c5665..6c19ea88 100644 --- a/class/ssh_authentication.py +++ b/class/ssh_authentication.py @@ -100,7 +100,7 @@ class ssh_authentication: def install_pam_python(self,check): so_path=check[2] so_name=check[2].split('/')[-1] - public.ExecShell('/usr/local/curl/bin/curl -o %s http://download.bt.cn/btwaf_rule/pam_python_so/%s'%(so_path,so_name)) + public.ExecShell('/usr/local/curl/bin/curl -o %s https://download.bt.cn/btwaf_rule/pam_python_so/%s'%(so_path,so_name)) public.ExecShell("chmod 600 " + so_path) return True diff --git a/class/ssh_security.py b/class/ssh_security.py index 0e648b6a..30e8876f 100644 --- a/class/ssh_security.py +++ b/class/ssh_security.py @@ -378,16 +378,28 @@ class ssh_security: 关闭key 无需参数传递 ''' - file = ['/root/.ssh/id_rsa.pub', '/root/.ssh/id_rsa', '/root/.ssh/authorized_keys'] - rec = '\n#?RSAAuthentication\s\w+' - rec2 = '\n#?PubkeyAuthentication\s\w+' - file = public.readFile(self.__SSH_CONFIG) - file_ssh = re.sub(rec, '\nRSAAuthentication no', file) - file_result = re.sub(rec2, '\nPubkeyAuthentication no', file_ssh) - self.wirte(self.__SSH_CONFIG, file_result) - self.set_password(get) - self.restart_ssh() - return public.returnMsg(True, 'Closed successfully') + is_ssh_status=public.get_sshd_status() + if is_ssh_status: + file = ['/root/.ssh/id_rsa.pub', '/root/.ssh/id_rsa', '/root/.ssh/authorized_keys'] + rec = '\n#?RSAAuthentication\s\w+' + rec2 = '\n#?PubkeyAuthentication\s\w+' + file = public.readFile(self.__SSH_CONFIG) + file_ssh = re.sub(rec, '\nRSAAuthentication no', file) + file_result = re.sub(rec2, '\nPubkeyAuthentication no', file_ssh) + self.wirte(self.__SSH_CONFIG, file_result) + self.set_password(get) + self.restart_ssh() + return public.returnMsg(True, 'Closed successfully') + else: + file = ['/root/.ssh/id_rsa.pub', '/root/.ssh/id_rsa', '/root/.ssh/authorized_keys'] + rec = '\n#?RSAAuthentication\s\w+' + rec2 = '\n#?PubkeyAuthentication\s\w+' + file = public.readFile(self.__SSH_CONFIG) + file_ssh = re.sub(rec, '\nRSAAuthentication no', file) + file_result = re.sub(rec2, '\nPubkeyAuthentication no', file_ssh) + self.wirte(self.__SSH_CONFIG, file_result) + #self.set_password(get) + return public.returnMsg(True, 'Closed successfully') def get_config(self, get): ''' @@ -514,6 +526,8 @@ if __name__ == '__main__': try: aa = ssh_security() aa.login() - except:pass + except: + print(111) + pass else: pass diff --git a/class/ssh_terminal.py b/class/ssh_terminal.py index 20763edb..9968da65 100644 --- a/class/ssh_terminal.py +++ b/class/ssh_terminal.py @@ -21,7 +21,7 @@ from io import BytesIO, StringIO def returnMsg(status,msg,value=None): if value: - msg = public.getMsg(msg,value) + msg = public.get_msg_gettext(msg,value) return {'status':status,'msg':msg} import public @@ -38,7 +38,7 @@ class ssh_terminal: _ssh = None _last_cmd = "" _last_cmd_tip = 0 - _log_type = public.getMsg('TYPE_TERMINAL') + _log_type = public.get_msg_gettext('aaPanel terminal') _history_len = 0 _client = "" _rep_ssh_config = False @@ -48,6 +48,8 @@ class ssh_terminal: _old_conf = None _debug_file = 'logs/terminal.log' _s_code = None + _last_num = 0 + _key_passwd = None def connect(self): ''' @@ -58,7 +60,7 @@ class ssh_terminal: msg: string 详情 } ''' - if not self._host: return returnMsg(False,'WRONG_CONN_ADDR') + if not self._host: return public.return_msg_gettext(False,'Wrong connection address') if not self._user: self._user = 'root' if not self._port: self._port = 22 @@ -71,7 +73,7 @@ class ssh_terminal: while num < 5: num +=1 try: - self.debug(public.getMsg('RECONN_TIMES',(num,))) + self.debug(public.get_msg_gettext('Reconnection attempts:{}',(num,))) if self._rep_ssh_config: time.sleep(0.1) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(2 + num) @@ -81,10 +83,10 @@ class ssh_terminal: except Exception as e: if num == 5: self.set_sshd_config(True) - self.debug(public.getMsg('RECONN_FAILED',(e,))) + self.debug(public.get_msg_gettext('Retry connection failed, {}',(e,))) if self._host in ['127.0.0.1','localhost']: - return returnMsg(False,'CONN_FAIL',("Authentication failed ," + self._user + "@" + self._host + ":" +str(self._port),)) - return returnMsg(False,'CONN_FAIL1',(self._host,self._port)) + return returnMsg(False,'Connection failure: {}',("Authentication failed ," + self._user + "@" + self._host + ":" +str(self._port),)) + return returnMsg(False,'Connection failure: {}',(self._host,self._port)) else: time.sleep(0.2) @@ -92,15 +94,15 @@ class ssh_terminal: import paramiko self._tp = paramiko.Transport(sock) - + pkey = None try: self._tp.start_client() if not self._pass and not self._pkey: self.set_sshd_config(True) - return public.returnMsg(False,'SSH_LOGIN_INFO_ERR',(self._host,str(self._port))) + return public.return_msg_gettext(False,'Password or private key cannot both be empty: {}:{}',(self._host,str(self._port))) self._tp.banner_timeout=60 if self._pkey: - self.debug(public.getMsg('AUTH_PRI_KEY')) + self.debug(public.get_msg_gettext('Authenticating private key')) if sys.version_info[0] == 2: try: self._pkey = self._pkey.encode('utf-8') @@ -110,18 +112,43 @@ class ssh_terminal: else: p_file = StringIO(self._pkey) try: - pkey = paramiko.RSAKey.from_private_key(p_file) - except: + if self._key_passwd: + pkey = paramiko.RSAKey.from_private_key(p_file,password=self._key_passwd) + else: + pkey = paramiko.RSAKey.from_private_key(p_file) + self.debug("尝试使用RSA私钥认证") + except Exception as ex: try: p_file.seek(0) - pkey = paramiko.Ed25519Key.from_private_key(p_file) + if self._key_passwd: + pkey = paramiko.Ed25519Key.from_private_key(p_file,password=self._key_passwd) + else: + pkey = paramiko.Ed25519Key.from_private_key(p_file) + self.debug("尝试使用Ed25519私钥认证") except: try: p_file.seek(0) - pkey = paramiko.ECDSAKey.from_private_key(p_file) + if self._key_passwd: + pkey = paramiko.ECDSAKey.from_private_key(p_file,password=self._key_passwd) + else: + pkey = paramiko.ECDSAKey.from_private_key(p_file) + self.debug("尝试使用ECDSA私钥认证") except: p_file.seek(0) - pkey = paramiko.DSSKey.from_private_key(p_file) + if self._key_passwd: + try: + pkey = paramiko.DSSKey.from_private_key(p_file,password=self._key_passwd) + except Exception as ex: + ex = str(ex) + if ex.find('OpenSSH private key file checkints do not match') != -1: + return public.returnMsg(False,'Incorrect private key password: {}'.format(ex)) + elif ex.find('encountered RSA key, expected DSA key') != -1: + pkey = paramiko.RSAKey.from_private_key(p_file,password=self._key_passwd) + else: + return public.returnMsg(False,'private key error: {}'.format(ex)) + else: + pkey = paramiko.DSSKey.from_private_key(p_file) + if not pkey: return public.returnMsg(False,'Incorrect private key!') self._tp.auth_publickey(username=self._user, key=pkey) else: try: @@ -142,32 +169,43 @@ class ssh_terminal: self._tp.close() e = str(e) if e.find('websocket error!') != -1: - return returnMsg(True,'connection succeeded') + return public.return_msg_gettext(True,'connection succeeded') if e.find('Authentication timeout') != -1: self.debug("认证超时{}".format(e)) - return returnMsg(False,'Authentication timed out, please press enter to try again!{}'.format(e)) + return public.return_msg_gettext(False,'Authentication timed out, please press enter to try again!{}',(e,)) if e.find('Authentication failed') != -1: - self.debug(public.getMsg('AUTH_FAIL',(str(e),))) - return returnMsg(False,'SSH_LOGIN_ERR1',(str(e + "," + self._user + "@" + self._host + ":" +str(self._port)),)) + self.debug(public.get_msg_gettext('Authentication failed {}',(str(e),))) + if self._key_passwd: + sshd_config = public.readFile('/etc/ssh/sshd_config') + if sshd_config and sshd_config.find('ssh-dss') == -1: + return returnMsg(False,'The private key verification fails, the private key may be incorrect, or the ssh-dss private key authentication type may not be enabled in the /etc/ssh/sshd_config configuration file') + return returnMsg(False,'Authentication failed, please check whether the private key is correct: {}'.format(e + "," + self._user + "@" + self._host + ":" +str(self._port))) + return returnMsg(False,'account or password incorrect:{}'.format(e + "," + self._user + "@" + self._host + ":" +str(self._port))) if e.find('Bad authentication type; allowed types') != -1: - self.debug(public.getMsg('AUTH_FAIL',(str(e),))) + self.debug(public.get_msg_gettext('Authentication failed {}',(str(e),))) if self._host in ['127.0.0.1','localhost'] and self._pass == 'none': - return returnMsg(False,'USER_OR_PASSWD_ERR',(str("Authentication failed ," + self._user + "@" + self._host + ":" +str(self._port)),)) - return returnMsg(False,'SSH_LOGIN_ERR2',(str(e))) + return public.return_msg_gettext(False,'Username or Password incorrect: {}',(str("Authentication failed ," + self._user + "@" + self._host + ":" +str(self._port)),)) + return public.return_msg_gettext(False,'Unsupported authentication type: {}',(str(e))) if e.find('Connection reset by peer') != -1: - self.debug(public.getMsg('SSH_LOGIN_ERR3')) - return returnMsg(False,public.getMsg('SSH_LOGIN_ERR3')) + self.debug(public.get_msg_gettext('The target server actively refused the connection')) + return public.return_msg_gettext(False,public.get_msg_gettext('The target server actively refused the connection')) if e.find('Error reading SSH protocol banner') != -1: - self.debug('SSH_LOGIN_ERR10') - return returnMsg(False,public.getMsg('SSH_LOGIN_ERR4',(str(e),))) + self.debug('The protocol header response timed out') + return public.return_msg_gettext(False,public.get_msg_gettext('The protocol header response timed out, and the network quality with the target server was too bad: {}',(str(e),))) + if e.find('encountered RSA key, expected DSA key') != -1: + self.debug('Private keys may require password access') + return public.return_msg_gettext(False,public.get_msg_gettext('Private keys may require password access: {}',(str(e),))) + if e.find('password and salt must not be empty') != -1: + self.debug('Private keys may require password access') + return public.return_msg_gettext(False,public.get_msg_gettext('Private keys may require password access: {}',(str(e),))) if not e: - self.debug('SSH_LOGIN_ERR11') - return returnMsg(False,"SSH_LOGIN_ERR5") + self.debug('The SSH protocol handshake timed out') + return public.return_msg_gettext(False,"The SSH protocol handshake timed out, and the network quality with the target server is too bad") err = public.get_error_info() self.debug(err) - return returnMsg(False,public.getMsg("SSH_LOGIN_ERR6",(str(err),))) + return public.return_msg_gettext(False,public.get_msg_gettext('unknown error: {}',(str(err),))) - self.debug('SSH_LOGIN_INFO3') + self.debug(public.get_msg_gettext('The authentication is successful and the session channel is being constructed')) self._ssh = self._tp.open_session() self._ssh.get_pty(term='xterm', width=100, height=34) self._ssh.invoke_shell() @@ -175,11 +213,11 @@ class ssh_terminal: self._last_send = [] from BTPanel import request self._client = public.GetClientIp() +':' + str(request.environ.get('REMOTE_PORT')) - public.WriteLog(self._log_type,'SSH_LOGIN',(self._host,str(self._port))) - self.history_send("LOGIN_SUCCESS2") + public.write_log_gettext(self._log_type,'Successfully logged in to the SSH server [{}:{}]',(self._host,str(self._port))) + self.history_send(public.get_msg_gettext("Login success\n")) self.set_sshd_config(True) - self.debug('SSH_LOGIN_INFO2') - return returnMsg(True,'CONNECTION_SUCCEEDED') + self.debug(public.get_msg_gettext('Login success')) + return public.return_msg_gettext(True,'connection succeeded') def _auth_interactive(self): self.debug('Verification Code') @@ -216,6 +254,8 @@ class ssh_terminal: self._tp.auth_interactive(self._user, handler) + + def get_login_user(self): ''' @name 获取本地登录用户 @@ -288,10 +328,15 @@ class ssh_terminal: self._pass = ssh_info['password'] self._old_conf = True return - + ssh_key_type_file = '{}/data/ssh_key_type.pl'.format(public.get_panel_path()) + ssh_key_type = '' + if os.path.exists(ssh_key_type_file): + ssh_key_type_new = public.readFile(ssh_key_type_file) + if ssh_key_type_new: ssh_key_type = ssh_key_type_new.strip() login_user = self.get_login_user() if self._user == 'root' and login_user == 'root': - id_rsa_file = ['/root/.ssh/id_rsa','/root/.ssh/id_rsa_bt'] + id_rsa_file = ['/root/.ssh/id_ed25519','/root/.ssh/id_ecdsa','/root/.ssh/id_rsa','/root/.ssh/id_rsa_bt'] + if ssh_key_type: id_rsa_file.insert(0,'/root/.ssh/id_{}'.format(ssh_key_type)) for ifile in id_rsa_file: if os.path.exists(ifile): self._pkey = public.readFile(ifile) @@ -301,12 +346,14 @@ class ssh_terminal: return + if not self._pass or not self._pkey or not self._user: home_path = '/home/' + login_user if login_user == 'root': home_path = '/root' self._user = login_user - id_rsa_file = [home_path + '/.ssh/id_rsa',home_path + '/.ssh/id_rsa_bt'] + id_rsa_file = [home_path + '/.ssh/id_ed25519',home_path + '/.ssh/id_ecdsa',home_path + '/.ssh/id_rsa',home_path + '/.ssh/id_rsa_bt'] + if ssh_key_type: id_rsa_file.insert(0,home_path + '/.ssh/id_{}'.format(ssh_key_type)) for ifile in id_rsa_file: if os.path.exists(ifile): self._pkey = public.readFile(ifile) @@ -314,19 +361,6 @@ class ssh_terminal: self._pass = 'none' return - # _ssh_ks = home_path + '/.ssh' - # if not os.path.exists(_ssh_ks): - # os.makedirs(_ssh_ks,384) - # os.system("ssh-keygen -t rsa -P '' -f {}/.ssh/id_rsa |echo y".format(home_path)) - # pub_file = home_path + '/.ssh/id_rsa.pub' - # az_file = home_path + '/.ssh/authorized_keys' - # rsa_file = home_path + '/.ssh/id_rsa' - # public.ExecShell('cat {} >> {} && chmod 600 {} {}'.format(pub_file, az_file, az_file,rsa_file)) - # os.remove(pub_file) - # public.ExecShell("chown -R {}:{} {}".format(self._user,self._user,_ssh_ks)) - # public.ExecShell("chmod -R 600 {}".format(_ssh_ks)) - # self._pkey = public.readFile(rsa_file) - except: return @@ -416,6 +450,8 @@ class ssh_terminal: self.restart_ssh() return True + + pin = r'^\s*PubkeyAuthentication\s+(yes|no)' pubkey_status = re.findall(pin,sshd_config,re.I) if pubkey_status: @@ -491,12 +527,12 @@ class ssh_terminal: ''' n = 0 try: - while not self._ws.closed: + while self._ws.connected: resp_line = self._ssh.recv(1024) if not resp_line: if not self._tp.is_active(): - self.debug(public.getMsg('SSH_LOGIN_ERR14')) - self._ws.send(public.getMsg('RECONNECT_SSH')) + self.debug(public.get_msg_gettext('Channel disconnected')) + self._ws.send(public.get_msg_gettext('The connection is disconnected, press enter to try to reconnect!')) self.close() return @@ -505,7 +541,7 @@ class ssh_terminal: if n > 5: break continue n = 0 - if self._ws.closed: + if not self._ws.connected: return try: result = resp_line.decode('utf-8','ignore') @@ -517,16 +553,16 @@ class ssh_terminal: self._ws.send(result) - self.history_recv(result) + # self.history_recv(result) except Exception as e: e = str(e) if e.find('closed') != -1: - self.debug('SSH_LOGIN_INFO') - elif not self._ws.closed: - self.debug(public.getMsg('SSH_LOGIN_ERR15',(str(e),))) + self.debug(public.getMsg('SSH_LOGIN_INFO')) + elif self._ws.connected: + self.debug(public.get_msg_gettext('Error reading tty buffer data, {}',(str(e),))) - if self._ws.closed: - self.debug(public.getMsg('SSH_LOGIN_INFO1')) + if not self._ws.connected: + self.debug(public.get_msg_gettext('The client has actively disconnected')) self.close() def send(self): @@ -536,12 +572,13 @@ class ssh_terminal: @return void ''' try: - while not self._ws.closed: + while self._ws.connected: if self._s_code: time.sleep(0.1) continue client_data = self._ws.receive() if not client_data: continue + if client_data == '{}': continue if len(client_data) > 10: if client_data.find('{"host":"') != -1: continue @@ -549,20 +586,21 @@ class ssh_terminal: self.resize(client_data) continue self._ssh.send(client_data) - self.history_send(client_data) + # self.history_send(client_data) except Exception as ex: ex = str(ex) + if ex.find('_io.BufferedReader') != -1: - self.debug(public.getMsg('SSH_LOGIN_ERR16')) + self.debug(public.get_msg_gettext('An error occurred while reading data from websocket. Retrying')) self.send() return elif ex.find('closed') != -1: - self.debug('SSH_LOGIN_INFO') + self.debug(public.get_msg_gettext('SSH_LOGIN_INFO')) else: - self.debug(public.getMsg('SSH_LOGIN_ERR17',(str(ex),))) + self.debug(public.get_msg_gettext('An error occurred while writing data to the buffer: {}',(str(ex),))) - if self._ws.closed: - self.debug(public.getMsg('SSH_LOGIN_INFO1')) + if not self._ws.connected: + self.debug(public.get_msg_gettext('The client has actively disconnected')) self.close() @@ -576,7 +614,7 @@ class ssh_terminal: #处理TAB补登 if self._last_cmd_tip == 1: if not recv_data.startswith('\r\n'): - self._last_cmd += recv_data.replace('\u0007','').strip() + self._last_cmd += recv_data.replace('\u0007','').replace("\x07","").strip() self._last_cmd_tip = 0 #上下切换命令 @@ -601,32 +639,49 @@ class ssh_terminal: self._last_cmd_tip = 2 return + #左移光标 + if send_data in ["\x1b[C"]: + self._last_num -= 1 + return + + # 右移光标 + if send_data in ["\x1b[D"]: + self._last_num += 1 + return + #退格 if send_data == "\x7f": self._last_cmd = self._last_cmd[:-1] return + #过滤特殊符号 - if send_data in ["\x1b[C","\x1b[D","\x1b[K","\x07","\x08","\x03","\x01","\x02","\x04","\x05","\x06","\u0007"]: + if send_data in ["\x1b[C","\x1b[D","\x1b[K","\x07","\x08","\x03","\x01","\x02","\x04","\x05","\x06","\x1bOB","\x1bOA","\x1b[8P","\x1b","\x1b[4P","\x1b[6P","\x1b[5P"]: return #Tab补全处理 - if send_data == '\t': + if send_data == "\t": self._last_cmd_tip = 1 return + if str(send_data).find("\x1b") != -1: + return + if send_data[-1] in ['\r','\n']: if not self._last_cmd: return his_shell = [int(time.time()),self._client,self._user,self._last_cmd] public.writeFile(his_file, json.dumps(his_shell) + "\n","a+") self._last_cmd = "" - #超过5M则保留最新的200行 - if os.stat(his_file).st_size > 5242880: - his_tmp = public.GetNumLines(his_file,200) + #超过50M则保留最新的20000行 + if os.stat(his_file).st_size > 52428800: + his_tmp = public.GetNumLines(his_file,20000) public.writeFile(his_file, his_tmp) else: - self._last_cmd += send_data + if self._last_num >= 0: + self._last_cmd += send_data + else: + self._last_cmd.insert(len(self._last_cmd) + self._last_num, send_data) def close(self): @@ -640,7 +695,7 @@ class ssh_terminal: self._ssh.close() if self._tp: # 关闭宿主服务 self._tp.close() - if not self._ws.closed: + if self._ws.connected: self._ws.close() except: pass @@ -660,6 +715,8 @@ class ssh_terminal: self._pkey = ssh_info['pkey'] if 'password' in ssh_info: self._pass = ssh_info['password'] + if 'pkey_passwd' in ssh_info: + self._key_passwd = ssh_info['pkey_passwd'] try: result = self.connect() except Exception as ex: @@ -680,7 +737,7 @@ class ssh_terminal: self._tp.send_ignore() else: break - if not self._ws.closed: + if self._ws.connected: self._ws.send("") else: break @@ -692,6 +749,7 @@ class ssh_terminal: @return void ''' msg = "{} - {}:{} => {} \n".format(public.format_date(),self._host,self._port,msg) + self.history_send(msg) public.writeFile(self._debug_file,msg,'a+') def run(self,web_socket, ssh_info=None): @@ -714,7 +772,8 @@ class ssh_terminal: return result = self.set_attr(ssh_info) else: - result = returnMsg(True,'ALREADY_CONNECTED') + result = public.get_msg_gettext(True,'ALREADY_CONNECTED') + if result['status']: sendt = threading.Thread(target=self.send) recvt = threading.Thread(target=self.recv) @@ -728,6 +787,7 @@ class ssh_terminal: self.close() else: self._ws.send(result['msg']) + self.close() def __del__(self): ''' @@ -746,6 +806,14 @@ class ssh_host_admin(ssh_terminal): _pass_str = None def __init__(self): + self.__create_aes_pass() + + def __create_aes_pass(self): + ''' + @name 创建AES密码 + @author + @return string + ''' if not os.path.exists(self._save_path): os.makedirs(self._save_path,384) if not os.path.exists(self._pass_file): @@ -753,6 +821,10 @@ class ssh_host_admin(ssh_terminal): public.set_mode(self._pass_file,600) if not self._pass_str: self._pass_str = public.readFile(self._pass_file) + if not self._pass_str: + self._pass_str = public.GetRandomString(16) + public.writeFile(self._pass_file,self._pass_str) + public.set_mode(self._pass_file,600) def get_host_list(self,args = None): ''' @@ -766,12 +838,17 @@ class ssh_host_admin(ssh_terminal): for name in os.listdir(self._save_path): info_file = self._save_path + name +'/info.json' if not os.path.exists(info_file): continue - info_tmp = self.get_ssh_info(name) - host_info = {} - host_info['host'] = name - host_info['port'] = info_tmp['port'] - host_info['ps'] = info_tmp['ps'] - host_info['sort'] = int(info_tmp['sort']) + try: + info_tmp = self.get_ssh_info(name) + host_info = {} + host_info['host'] = name + host_info['port'] = info_tmp['port'] + host_info['ps'] = info_tmp['ps'] + host_info['sort'] = int(info_tmp['sort']) + except: + if os.path.exists(info_file): + os.remove(info_file) + continue host_list.append(host_info) @@ -790,7 +867,7 @@ class ssh_host_admin(ssh_terminal): args.host = args.host.strip() info_file = self._save_path + args.host +'/info.json' if not os.path.exists(info_file): - return public.returnMsg(False,'SSH_LOGIN_ERR7') + return public.return_msg_gettext(False,'The specified SSH information does not exist!') info_tmp = self.get_ssh_info(args.host) host_info = {} host_info['host'] = args.host @@ -800,6 +877,9 @@ class ssh_host_admin(ssh_terminal): host_info['username'] = info_tmp['username'] host_info['password'] = info_tmp['password'] host_info['pkey'] = info_tmp['pkey'] + host_info['pkey_passwd'] = '' + if 'pkey_passwd' in info_tmp: + host_info['pkey_passwd'] = info_tmp['pkey_passwd'] return host_info def modify_host(self,args): @@ -815,6 +895,7 @@ class ssh_host_admin(ssh_terminal): username: 用户名 password: 密码 pkey: 密钥(如果不为空,将使用密钥连接) + pkey_passwd: 密钥的密码 } @return dict ''' @@ -823,12 +904,12 @@ class ssh_host_admin(ssh_terminal): if args.host != args.new_host: info_file = self._save_path + args.new_host +'/info.json' if os.path.exists(info_file): - return public.returnMsg(False,'SSH_LOGIN_ERR8') + return public.return_msg_gettext(False,'The specified host address has been added to other SSH information!') info_file = self._save_path + args.host +'/info.json' if not os.path.exists(info_file): - return public.returnMsg(False,'SSH_LOGIN_ERR7') + return public.return_msg_gettext(False,'The specified SSH information does not exist!') if not 'sort' in args: r_data = public.aes_decrypt(public.readFile(info_file),self._pass_str) @@ -843,14 +924,18 @@ class ssh_host_admin(ssh_terminal): host_info['username'] = args['username'] host_info['password'] = args['password'] host_info['pkey'] = args['pkey'] + if 'pkey_passwd' in args: + host_info['pkey_passwd'] = args['pkey_passwd'] + else: + host_info['pkey_passwd'] = '' if not host_info['pkey']: host_info['pkey'] = '' result = self.set_attr(host_info) if not result['status']: return result self.save_ssh_info(args.host,host_info) if args.host != args.new_host: public.ExecShell('mv {} {}'.format(self._save_path + args.host,self._save_path + args.new_host)) - public.WriteLog(self._log_type,'MODIFY_SSH_INFO',(args.host,)) - return public.returnMsg(True,'EDIT_SUCCESS') + public.write_log_gettext(self._log_type,'Modify the SSH information of HOST: {}',(args.host,)) + return public.return_msg_gettext(True,'Setup successfully!') def create_host(self,args): ''' @@ -864,6 +949,7 @@ class ssh_host_admin(ssh_terminal): username: 用户名 password: 密码 pkey: 密钥(如果不为空,将使用密钥连接) + pkey_passwd: 密钥的密码 } @return dict ''' @@ -886,11 +972,14 @@ class ssh_host_admin(ssh_terminal): host_info['username'] = args['username'] host_info['password'] = args['password'] host_info['pkey'] = args['pkey'] + host_info['pkey_passwd'] = '' + if 'pkey_passwd' in args: + host_info['pkey_passwd'] = args['pkey_passwd'] result = self.set_attr(host_info) if not result['status']: return result self.save_ssh_info(args.host,host_info) - public.WriteLog(self._log_type,'ADD_SSH_INFO',(str(args.host),)) - return public.returnMsg(True,'SET_SUCCESS') + public.write_log_gettext(self._log_type,'Add the SSH information of HOST: {}',(str(args.host),)) + return public.return_msg_gettext(True,'Setup successfully!') def remove_host(self,args): @@ -903,13 +992,13 @@ class ssh_host_admin(ssh_terminal): @return dict ''' args.host = args.host.strip() - if not args.host: return public.returnMsg(False,'INIT_ARGS_ERR') + if not args.host: return public.return_msg_gettext(False,'Parameter ERROR!') host_path = self._save_path + args.host if not os.path.exists(host_path): - return public.returnMsg(False,'SSH_LOGIN_ERR7!') + return public.return_msg_gettext(False,'The specified SSH information does not exist!') public.ExecShell("rm -rf {}".format(host_path)) - public.WriteLog(self._log_type,'DEL_SSH_INFO',(str(args.host),)) - return public.returnMsg(True,'SET_SUCCESS') + public.write_log_gettext(self._log_type,'Delete the SSH information of HOST: {}',(str(args.host),)) + return public.return_msg_gettext(True,'Setup successfully!') def get_ssh_info(self,host): @@ -921,7 +1010,15 @@ class ssh_host_admin(ssh_terminal): ''' info_file = self._save_path + host + '/info.json' if not os.path.exists(info_file): return False - r_data = public.aes_decrypt(public.readFile(info_file),self._pass_str) + try: + r_data = public.aes_decrypt(public.readFile(info_file),self._pass_str) + except ValueError as ex: + if str(ex).find('Incorrect AES key length') != -1: + if os.path.exists(self._pass_file): + os.remove(self._pass_file) + self.__create_aes_pass() + r_data = public.aes_decrypt(public.readFile(info_file),self._pass_str) + return json.loads(r_data) def save_ssh_info(self,host,host_info): @@ -954,7 +1051,7 @@ class ssh_host_admin(ssh_terminal): @return bool ''' if not 'sort_list' in args: - return public.returnMsg(False,'SSH_LOGIN_ERR9') + return public.return_msg_gettext(False,'Please pass in the [sort_list] field') sort_list = json.loads(args.sort_list) for name in sort_list.keys(): info_file = self._save_path + name + '/info.json' @@ -963,7 +1060,7 @@ class ssh_host_admin(ssh_terminal): ssh_info = self.get_ssh_info(name) ssh_info['sort'] = int(sort_list[name]) self.save_ssh_info(name,ssh_info) - return public.returnMsg(True,'SET_SUCCESS') + return public.return_msg_gettext(True,'Setup successfully!') def get_command_list(self,args = None, user_cmd = False , sys_cmd = False): ''' @@ -1028,7 +1125,7 @@ class ssh_host_admin(ssh_terminal): command = self.get_command_list(sys_cmd=True) if self.command_exists(command,args.title): - return public.returnMsg(False,'COMMAND_NAME_EXIST') + return public.return_msg_gettext(False,'The specified command name already exists') cmd = { "title": args.title, @@ -1037,8 +1134,8 @@ class ssh_host_admin(ssh_terminal): command.append(cmd) self.save_command(command) - public.WriteLog(self._log_type,'ADD_COMMAND_COMMAND',(str(args.title),)) - return public.returnMsg(True,'SET_SUCCESS') + public.write_log_gettext(self._log_type,'Add common commands [{}]',(str(args.title),)) + return public.return_msg_gettext(True,'Setup successfully!') def get_command_find(self,args = None, title=None): ''' @@ -1053,9 +1150,9 @@ class ssh_host_admin(ssh_terminal): if args: title = args.title.strip() command = self.get_command_list() for cmd in command: - if cmd['title'] == title: + if cmd['title'] == title or cmd['title'] == args.title: return cmd - return public.returnMsg(False,'COMMAND_NOTEXIST') + return public.return_msg_gettext(False,'The specified command does not exist') def modify_command(self,args): ''' @@ -1068,18 +1165,18 @@ class ssh_host_admin(ssh_terminal): } @return dict ''' - args.title = args.title.strip() + title = args.title.strip() command = self.get_command_list(sys_cmd=True) if not self.command_exists(command,args.title): - return public.returnMsg(False,'COMMAND_NOTEXIST') + return public.return_msg_gettext(False,'The specified command does not exist') for i in range(len(command)): - if command[i]['title'] == args.title: - command[i]['title'] = args.new_title + if command[i]['title'] == args.title or command[i]['title'] == title: + command[i]['title'] = args.new_title.strip() command[i]['shell'] = args.shell.strip() break self.save_command(command) - public.WriteLog(self._log_type,'EDIT_COMMAND_COMMAND',(str(args.title),)) - return public.returnMsg(True,'SET_SUCCESS') + public.write_log_gettext(self._log_type,'Modify common commands [{}]',(str(args.title),)) + return public.return_msg_gettext(True,'Setup successfully!') def remove_command(self,args): ''' @@ -1093,12 +1190,12 @@ class ssh_host_admin(ssh_terminal): args.title = args.title.strip() command = self.get_command_list(sys_cmd=True) if not self.command_exists(command,args.title): - return public.returnMsg(False,'COMMAND_NOTEXIST') + return public.return_msg_gettext(False,'The specified command does not exist') for i in range(len(command)): if command[i]['title'] == args.title: del(command[i]) break self.save_command(command) - public.WriteLog(self._log_type,'DEL_COMMAND_COMMAND',(str(args.title),)) - return public.returnMsg(True,'SET_SUCCESS') \ No newline at end of file + public.write_log_gettext(self._log_type,'Delete common commands [{}]',(str(args.title),)) + return public.return_msg_gettext(True,'Setup successfully!') \ No newline at end of file diff --git a/class/system.py b/class/system.py index 00a12d5e..16989e60 100644 --- a/class/system.py +++ b/class/system.py @@ -28,7 +28,7 @@ class system: data = session['config'] data['webserver'] = public.get_webserver() #PHP版本 - phpVersions = ('52','53','54','55','56','70','71','72','73','74') + phpVersions = public.get_php_versions() data['php'] = [] @@ -120,7 +120,7 @@ class system: tmp['type'] = data['webserver'] - tmp['version'] = public.readFile(self.setupPath + '/'+data['webserver']+'/version.pl'); + tmp['version'] = public.xss_version(public.readFile(self.setupPath + '/'+data['webserver']+'/version.pl')) tmp['status'] = False result = public.ExecShell('/etc/init.d/' + serviceName + ' status') if result[0].find('running') != -1: tmp['status'] = True @@ -128,7 +128,7 @@ class system: tmp = {} vfile = self.setupPath + '/phpmyadmin/version.pl' - tmp['version'] = public.readFile(vfile) + tmp['version'] = public.xss_version(public.readFile(vfile)) if tmp['version']: tmp['version'] = tmp['version'].strip() tmp['setup'] = os.path.exists(vfile) tmp['status'] = pstatus @@ -141,12 +141,12 @@ class system: tmp['setup'] = os.path.exists('/etc/init.d/tomcat') tmp['status'] = tmp['setup'] #if public.ExecShell('ps -aux|grep tomcat|grep -v grep')[0] == "": tmp['status'] = False - tmp['version'] = public.readFile(self.setupPath + '/tomcat/version.pl') + tmp['version'] = public.xss_version(public.readFile(self.setupPath + '/tomcat/version.pl')) data['tomcat'] = tmp tmp = {} tmp['setup'] = os.path.exists(self.setupPath +'/mysql/bin/mysql') - tmp['version'] = public.readFile(self.setupPath + '/mysql/version.pl') + tmp['version'] = public.xss_version(public.readFile(self.setupPath + '/mysql/version.pl')) tmp['status'] = os.path.exists('/tmp/mysql.sock') data['mysql'] = tmp @@ -162,7 +162,7 @@ class system: tmp = {} tmp['setup'] = os.path.exists(self.setupPath +'/pure-ftpd/bin/pure-pw') - tmp['version'] = public.readFile(self.setupPath + '/pure-ftpd/version.pl') + tmp['version'] = public.xss_version(public.readFile(self.setupPath + '/pure-ftpd/version.pl')) tmp['status'] = os.path.exists('/var/run/pure-ftpd.pid') data['pure-ftpd'] = tmp data['panel'] = self.GetPanelInfo() @@ -281,14 +281,7 @@ class system: key = 'sys_version' version = cache.get(key) if version: return version - import public - version = public.readFile('/etc/redhat-release') - if not version: - version = public.readFile('/etc/issue').strip().split("\n")[0].replace('\\n','').replace('\l','').strip() - else: - version = version.replace('release ','').replace('Linux','').replace('(Core)','').strip() - v_info = sys.version_info - version = version + '(Py' + str(v_info.major) + '.' + str(v_info.minor) + '.' + str(v_info.micro) + ')' + version = public.get_os_version() cache.set(key,version,600) return version @@ -396,13 +389,17 @@ class system: diskInfo.append(tmp) return diskInfo - def GetDiskInfo2(self): + def GetDiskInfo2(self, human=True): + #取磁盘分区信息 key = 'sys_disk' diskInfo = cache.get(key) if diskInfo: return diskInfo - temp = public.ExecShell("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")[0] - tempInodes = public.ExecShell("df -i -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")[0] + if human: + temp = public.ExecShell("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev|grep -v overlay")[0] + else: + temp = public.ExecShell("df -T -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev|grep -v overlay")[0] + tempInodes = public.ExecShell("df -i -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev|grep -v overlay")[0] temp1 = temp.split('\n') tempInodes1 = tempInodes.split('\n') diskInfo = [] @@ -432,7 +429,7 @@ class system: arr['inodes'] = [inodes[1],inodes[2],inodes[3],inodes[4]] diskInfo.append(arr) except Exception as ex: - public.WriteLog('GET_INFO',str(ex)) + public.write_log_gettext('Get Info',str(ex)) continue cache.set(key,diskInfo,10) return diskInfo @@ -809,7 +806,7 @@ class system: import ajax get.status = 'True' ajax.ajax().setPHPMyAdmin(get) - return public.returnMsg(True,'SYS_EXEC_SUCCESS') + return public.return_msg_gettext(True,'Executed successfully!') if get.name == 'openlitespeed': if get.type == 'stop': @@ -818,12 +815,12 @@ class system: public.ExecShell('rm -f /tmp/lshttpd/*.sock* && /usr/local/lsws/bin/lswsctrl start') else: public.ExecShell('rm -f /tmp/lshttpd/*.sock* && /usr/local/lsws/bin/lswsctrl restart') - return public.returnMsg(True,'SYS_EXEC_SUCCESS') + return public.return_msg_gettext(True,'Executed successfully!') #检查httpd配置文件 if get.name == 'apache' or get.name == 'httpd': get.name = 'httpd' - if not os.path.exists(self.setupPath+'/apache/bin/apachectl'): return public.returnMsg(True,'SYS_NOT_INSTALL_APACHE') + if not os.path.exists(self.setupPath+'/apache/bin/apachectl'): return public.return_msg_gettext(True,'Execution failed, check if Apache installed') vhostPath = self.setupPath + '/panel/vhost/apache' if not os.path.exists(vhostPath): public.ExecShell('mkdir ' + vhostPath) @@ -835,8 +832,8 @@ class system: result = public.ExecShell('ulimit -n 8192 ; ' + self.setupPath+'/apache/bin/apachectl -t') if result[1].find('Syntax OK') == -1: - public.WriteLog("TYPE_SOFT",'SYS_EXEC_ERR', (str(result),)) - return public.returnMsg(False,'SYS_CONF_APACHE_ERR',(result[1].replace("\n",'
                                      '),)) + public.write_log_gettext("Software manager",'Execution failed: {}', (str(result),)) + return public.return_msg_gettext(False,"Apache rule configuration error:
                                      {}",(result[1].replace("\n",'
                                      '),)) if get.type == 'restart': public.ExecShell('pkill -9 httpd') @@ -847,12 +844,14 @@ class system: elif get.name == 'nginx': vhostPath = self.setupPath + '/panel/vhost/rewrite' if not os.path.exists(vhostPath): public.ExecShell('mkdir ' + vhostPath) + if not os.path.exists("/dev/shm/nginx-cache/wp"): + public.ExecShell('mkdir -p /dev/shm/nginx-cache/wp && chown -R www.www /dev/shm/nginx-cache') vhostPath = self.setupPath + '/panel/vhost/nginx' if not os.path.exists(vhostPath): public.ExecShell('mkdir ' + vhostPath) public.ExecShell('/etc/init.d/nginx start') - result = public.ExecShell('ulimit -n 8192 ; nginx -t -c '+self.setupPath+'/nginx/conf/nginx.conf') + result = public.ExecShell('ulimit -n 8192 ; '+self.setupPath+'/nginx/sbin/nginx -t -c '+self.setupPath+'/nginx/conf/nginx.conf') if result[1].find('perserver') != -1: limit = self.setupPath + '/nginx/conf/nginx.conf' nginxConf = public.readFile(limit) @@ -860,18 +859,18 @@ class system: nginxConf = nginxConf.replace("#limit_conn_zone $binary_remote_addr zone=perip:10m;",limitConf) public.writeFile(limit,nginxConf) public.ExecShell('/etc/init.d/nginx start') - return public.returnMsg(True,'SYS_CONF_NGINX_REP') + return public.return_msg_gettext(True,'Configuration file mismatch caused by reinstalling Nginx fixed') if result[1].find('proxy') != -1: import panelSite panelSite.panelSite().CheckProxy(get) public.ExecShell('/etc/init.d/nginx start') - return public.returnMsg(True,'SYS_CONF_NGINX_REP') + return public.return_msg_gettext(True,'Configuration file mismatch caused by reinstalling Nginx fixed') #return result if result[1].find('successful') == -1: - public.WriteLog("TYPE_SOFT",'SYS_EXEC_ERR', (str(result),)) - return public.returnMsg(False,'SYS_CONF_NGINX_ERR',(result[1].replace("\n",'
                                      '),)) + public.write_log_gettext("Software manager",'Execution failed: {}', (str(result),)) + return public.return_msg_gettext(False,"Nginx rule configuration error:
                                      {}",(result[1].replace("\n",'
                                      '),)) if get.type == 'start': self.kill_port() @@ -903,14 +902,14 @@ class system: public.ExecShell('pkill -9 nginx && sleep 1') public.ExecShell('/etc/init.d/nginx start') if get.type != 'test': - public.WriteLog("TYPE_SOFT", 'SYS_EXEC_SUCCESS',(execStr,)) - if len(result[1]) > 1 and get.name != 'pure-ftpd' and get.name != 'redis': return public.returnMsg(False, '

                                      Warning message:

                                      ' + result[1].replace('\n','
                                      ')) - return public.returnMsg(True,'SYS_EXEC_SUCCESS') + public.write_log_gettext("Software manager", 'Executed successfully!',(execStr,)) + if len(result[1]) > 1 and get.name != 'pure-ftpd' and get.name != 'redis': return public.return_msg_gettext(False, '

                                      Warning message:

                                      ' + result[1].replace('\n','
                                      ')) + return public.return_msg_gettext(True,'Executed successfully!') def RestartServer(self,get): - if not public.IsRestart(): return public.returnMsg(False,'EXEC_ERR_TASK') + if not public.IsRestart(): return public.return_msg_gettext(False,'Please run the program when all install tasks finished!') public.ExecShell("sync && init 6 &") - return public.returnMsg(True,'SYS_REBOOT') + return public.return_msg_gettext(True,'Command sent successfully!') def kill_port(self): public.ExecShell('pkill -9 httpd') @@ -931,7 +930,7 @@ class system: def ReWeb(self,get): public.ExecShell("/etc/init.d/bt start") public.writeFile('data/restart.pl','True') - return public.returnMsg(True,'PANEL_WAS_RESTART') + return public.return_msg_gettext(True,'Panel restarted') #修复面板 diff --git a/class/tomcat.py b/class/tomcat.py index 8488ad52..4456e8bf 100644 --- a/class/tomcat.py +++ b/class/tomcat.py @@ -27,8 +27,11 @@ class tomcat: self.__ENGINE = self.__TREE.findall('Service/Engine')[0]; #获取虚拟主机列表 - def GetVhosts(self): - Hosts = self.__ENGINE.getchildren(); + def GetVhosts(self): + try: + Hosts = self.__ENGINE.getchildren() + except: + Hosts = list(self.__ENGINE) data = [] for host in Hosts: if host.tag != 'Host': continue; @@ -63,7 +66,10 @@ class tomcat: #获取指定虚拟主机 def GetVhost(self,name): - Hosts = self.__ENGINE.getchildren(); + try: + Hosts = self.__ENGINE.getchildren() + except: + Hosts = list(self.__ENGINE) for host in Hosts: if host.tag != 'Host': continue; if host.attrib['name'] == name: diff --git a/class/tools.py b/class/tools.py index d81767c9..d8358a18 100644 --- a/class/tools.py +++ b/class/tools.py @@ -65,8 +65,8 @@ def set_panel_pwd(password,ncli = False): result = sql.table('users').where('id=?',(1,)).setField('password',public.md5(password)) username = sql.table('users').where('id=?',(1,)).getField('username') if ncli: - print("|-%s: " % public.GetMsg("USER_NAME") + username) - print("|-%s: " % public.GetMsg("NEW_PASS") + password) + print("|-%s: " % public.get_msg_gettext('Username') + username) + print("|-%s: " % public.get_msg_gettext('New password') + password) else: print(username) @@ -107,28 +107,28 @@ echo '---------------------------------------------------------------------' #封装 def PackagePanel(): print('========================================================') - print('|-%s...' % public.GetMsg("CLEARING_LOG")), - public.M('logs').where('id!=?',(0,)).delete(); + print('|-%s...' % public.get_msg_gettext('Clearing log info')), + public.M('logs').where('id!=?',(0,)).delete() print('\t\t\033[1;32m[done]\033[0m') - print('|-%s...' % public.GetMsg("CLEARING_TASK_HISTORY")), - public.M('tasks').where('id!=?',(0,)).delete(); + print('|-%s...' % public.get_msg_gettext('Clearing task history')), + public.M('tasks').where('id!=?',(0,)).delete() print('\t\t\033[1;32m[done]\033[0m') - print('|-%s...' % public.GetMsg("CLEARING_NET_MO")), - public.M('network').dbfile('system').where('id!=?',(0,)).delete(); + print('|-%s...' % public.get_msg_gettext('Clearing network monitoring records')), + public.M('network').dbfile('system').where('id!=?',(0,)).delete() print('\t\033[1;32m[done]\033[0m') - print('|-%s...' % public.GetMsg("CLEARING_CPU_MO")), - public.M('cpuio').dbfile('system').where('id!=?',(0,)).delete(); + print('|-%s...' % public.get_msg_gettext('Clearing CPU monitoring records')), + public.M('cpuio').dbfile('system').where('id!=?',(0,)).delete() print('\t\033[1;32m[done]\033[0m') - print('|-%s...' % public.GetMsg("CLEARING_DISK_MO")), - public.M('diskio').dbfile('system').where('id!=?',(0,)).delete(); + print('|-%s...' % public.get_msg_gettext('Clearing disk monitoring records')), + public.M('diskio').dbfile('system').where('id!=?',(0,)).delete() print('\t\033[1;32m[done]\033[0m') - print('|-%s...' % public.GetMsg('CLEARING_IP')), + print('|-%s...' % public.get_msg_gettext('Clearing IP info')), os.system('rm -f /www/server/panel/data/iplist.txt') os.system('rm -f /www/server/panel/data/address.pl') os.system('rm -f /www/server/panel/data/*.login') os.system('rm -f /www/server/panel/data/domain.conf') print('\t\033[1;32m[done]\033[0m') - print('|-%s...' % public.GetMsg("CLEARING_SYS_HISTORY")), + print('|-%s...' % public.get_msg_gettext('Clearing system history')), command = '''cat /dev/null > /var/log/boot.log cat /dev/null > /var/log/btmp cat /dev/null > /var/log/cron @@ -154,8 +154,8 @@ history -c port = public.readFile('data/port.pl').strip(); public.M('config').where("id=?",('1',)).setField('status',0); print('========================================================') - print('\033[1;32m|-%s\033[0m' % public.GetMsg("PANEL_TIPS")) - print('\033[1;41m|-%s: http://{SERVERIP}:' % public.GetMsg("PANEL_INIT_ADD")+port+'/install\033[0m') + print('\033[1;32m|-%s\033[0m' % public.get_msg_gettext('The panel is packaged successfully. Please do NOT log in to the panel to do any other operations!')) + print('\033[1;41m|-%s: http://{SERVERIP}:' % public.get_msg_gettext('Panel initialization address')+port+'/install\033[0m') #清空正在执行的任务 def CloseTask(): @@ -163,7 +163,7 @@ def CloseTask(): os.system("kill `ps -ef |grep 'python panelSafe.pyc'|grep -v grep|grep -v panelExec|awk '{print $2}'`"); os.system("kill `ps -ef |grep 'install_soft.sh'|grep -v grep|grep -v panelExec|awk '{print $2}'`"); os.system('/etc/init.d/bt restart'); - print(public.GetMsg("CLEAR_TASK",(int(ncount),))) + print(public.get_msg_gettext('Successfully cleared {} tasks!',(int(ncount),))) #自签证书 def CreateSSL(): @@ -217,7 +217,7 @@ def ClearSystem(): count += tmp_count; total += tmp_total; print('=======================================================================') - print('\033[1;32m|-%s\033[0m' % public.GetMsg("CLEAR_RUBBISH",(str(count),ToSize(total)))); + print('\033[1;32m|-%s\033[0m' % public.get_msg_gettext('System rubbish cleared, totally deleted [{}] files, freed disk space [{}]',(str(count),ToSize(total)))); #清理邮件日志 def ClearMail(): @@ -242,11 +242,11 @@ def ClearMail(): os.remove(filename) print('\t\033[1;32m[OK]\033[0m') num += 1 - print(public.GetMsg("CLEAR_RUBBISH1",(dpath,str(num),ToSize(size)))) + print(public.get_msg_gettext('|-Cleared [{}], deleted [{}] files, freed disk space [{}]',(dpath,str(num),ToSize(size)))) total += size; count += num; print('=======================================================================') - print(public.GetMsg('CLEAR_RUBBISH2',(str(count),ToSize(total)))) + print(public.get_msg_gettext('|-Spool cleared, deleted [{}] files, freed disk space [{}]',(str(count),ToSize(total)))) return total,count #清理php_session文件 @@ -254,7 +254,7 @@ def ClearSession(): spath = '/tmp' total = count = 0; import shutil - print(public.GetMsg("CLEAR_PHP_SESSION")); + print(public.get_msg_gettext('|-Clearing PHP Session ...')); for d in os.listdir(spath): if d.find('sess_') == -1: continue; filename = spath + '/' + d; @@ -267,7 +267,7 @@ def ClearSession(): os.remove(filename) print('\t\033[1;32m[OK]\033[0m') count += 1; - print(public.GetMsg("CLEAR_PHP_SESSION1",(str(count),ToSize(total)))) + print(public.get_msg_gettext('|-PHP session cleared, deleted [{}] files, freed disk space [{}]',(str(count),ToSize(total)))) return total,count #清空回收站 @@ -288,7 +288,7 @@ def ClearOther(): ] total = count = 0; - print(public.GetMsg('CLEAR_RUBBISH3')); + print(public.get_msg_gettext('|-Clearing up temporary files and site logs...')); for c in clearPath: for d in os.listdir(c['path']): if d.find(c['find']) == -1: continue; @@ -304,7 +304,7 @@ def ClearOther(): count += 1; public.serviceReload(); os.system('sleep 1 && /etc/init.d/bt reload > /dev/null &'); - print(public.GetMsg("CLEAR_RUBBISH4",(str(count),ToSize(total)))) + print(public.get_msg_gettext('|-Temporary files and site logs cleared, deleted [{}] files, freed disk space [{}]',(str(count),ToSize(total)))) return total,count #关闭普通日志 @@ -336,14 +336,14 @@ def set_panel_username(username = None): sql = db.Sql() if username: if len(username) < 5: - print(public.GetMsg("USER_NAME_LEN_ERR")) + print(public.get_msg_gettext('|-ERROR, username cannot be less than 5 characters')) return; if username in ['admin','root']: - print(public.GetMsg("EASY_NAME")) + print(public.get_msg_gettext('|-ERROR, cannot use too simple username')) return; sql.table('users').where('id=?',(1,)).setField('username',username) - print(public.GetMsg("NEW_NAME",(username,))) + print(public.get_msg_gettext('|-New username: {}',(username,))) return; username = sql.table('users').where('id=?',(1,)).getField('username') @@ -365,14 +365,14 @@ def setup_idc(): pFile = panelPath + '/static/language/Simplified_Chinese/public.json' pInfo = json.loads(public.readFile(pFile)) pInfo['BRAND'] = idcInfo['msg']['name'] - pInfo['PRODUCT'] = public.GetMsg("WITH_BT_CUSTOM_EDITION") + pInfo['PRODUCT'] = public.get_msg_gettext('Customized edition with aaPanel') pInfo['NANE'] = pInfo['BRAND'] + pInfo['PRODUCT'] public.writeFile(pFile,json.dumps(pInfo)) tFile = panelPath + '/data/title.pl' - titleNew = (pInfo['BRAND'] + public.GetMsg("PANEL")).encode('utf-8') + titleNew = (pInfo['BRAND'] + public.get_msg_gettext('Failed,cannot delete current port of the panel!')).encode('utf-8') if os.path.exists(tFile): title = public.readFile(tFile).strip() - if title == public.GetMsg("NAME") or title == '': public.writeFile(tFile,titleNew) + if title == public.get_msg_gettext('aaPanel') or title == '': public.writeFile(tFile,titleNew) else: public.writeFile(tFile,titleNew) return True @@ -381,48 +381,48 @@ def setup_idc(): #将插件升级到6.0 def update_to6(): print("====================================================") - print(public.GetMsg("PLUG_UPDATEING")) + print(public.get_msg_gettext('Updating plugin...')) print("====================================================") download_address = public.get_url() exlodes = ['gitlab','pm2','mongodb','deployment_jd','logs','docker','beta','btyw'] for pname in os.listdir('plugin/'): if not os.path.isdir('plugin/' + pname): continue if pname in exlodes: continue - print("|-正在升级【%s】..." % pname), - download_url = download_address + '/install/plugin/' + pname + '/install.sh'; + print(public.get_msg_gettext("|-Upgrading [{}]...",(pname,))), + download_url = download_address + '/install/plugin/' + pname + '/install.sh' to_file = '/tmp/%s.sh' % pname - public.downloadFile(download_url,to_file); - os.system('/bin/bash ' + to_file + ' install &> /tmp/plugin_update.log 2>&1'); - print(" \033[32m[%s]\033[0m" % public.GetMsg("SUCCESS")) + public.downloadFile(download_url,to_file) + os.system('/bin/bash ' + to_file + ' install &> /tmp/plugin_update.log 2>&1') + print(" \033[32m[%s]\033[0m" % public.get_msg_gettext('Login succeeded, loading...')) print("====================================================") - print("\033[32m%s\033[0m" % public.GetMsg("PLUG_UPDATE_TO_6")) + print("\033[32m%s\033[0m" % public.get_msg_gettext('All plugins successfully updated to 6.0 compatible!')) print("====================================================") #命令行菜单 def bt_cli(): raw_tip = "===============================================" - print("===============%s==================" % public.GetMsg("PANEL_SHELL")) - print("(01) %s (08) %s" % (public.GetMsg("RESTART_PANEL"),public.GetMsg("CHANGE_PANEL_PORT"))) - print("(02) %s (09) %s" % (public.GetMsg("STOP_PANEL"),public.GetMsg("CLEAR_PANEL_CACHE"))) - print("(03) %s (10) %s" % (public.GetMsg("START_PANEL"),public.GetMsg("CLEAR_PANEL_LIMIT"))) - print("(04) %s (11) %s" % (public.GetMsg("RELOAD_PANEL"),public.GetMsg("CANCEL_ENTRY"))) - print("(05) %s (12) %s" % (public.GetMsg("CHANGE_PANEL_PASS"),public.GetMsg("CANCEL_DOMAIN_BIND"))) - print("(06) %s (13) %s" % (public.GetMsg("CHANGE_PANEL_USER"),public.GetMsg("CANCEL_IP_LIMIT"))) - print("(07) %s (14) %s" % (public.GetMsg("CHANGE_MYSQL_PASS_FORCE"),public.GetMsg("GET_PANEL_DEFAULT_MSG"))) - print("(00) %s (15) %s" % (public.GetMsg("CANCEL"),public.GetMsg("CLEAR_SYS_RUBBISH"))) + print("===============%s==================" % public.get_msg_gettext('aaPanel CLI')) + print("(01) %s (08) %s" % (public.get_msg_gettext('Restart panel'),public.get_msg_gettext('Change panel port'))) + print("(02) %s (09) %s" % (public.get_msg_gettext('Stop panel'),public.get_msg_gettext('Clear panel cache'))) + print("(03) %s (10) %s" % (public.get_msg_gettext('Restart panel'),public.get_msg_gettext('Clear login limit'))) + print("(04) %s (11) %s" % (public.get_msg_gettext('Reload panel'),public.get_msg_gettext('Cancel entrance limit'))) + print("(05) %s (12) %s" % (public.get_msg_gettext('Change panel password'),public.get_msg_gettext('Cancel domain binding limit'))) + print("(06) %s (13) %s" % (public.get_msg_gettext('Change panel username'),public.get_msg_gettext('Cacel IP access limit'))) + print("(07) %s (14) %s" % (public.get_msg_gettext('Forcibly change MySQL root password'),public.get_msg_gettext('View panel default info'))) + print("(00) %s (15) %s" % (public.get_msg_gettext('Task cancelled!'),public.get_msg_gettext('Clear system rubbish'))) print(raw_tip) try: - u_input = input(public.GetMsg("INPUT_CMD_NUM")) + u_input = input(public.get_msg_gettext('Pls enter command number:')) 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] if not u_input in nums: print(raw_tip) - print(public.GetMsg("CANCELLED")) + print(public.get_msg_gettext('Cacelled!')) exit() print(raw_tip) - print(public.GetMsg("EXECUTING",(u_input,))) + print(public.get_msg_gettext('Executing ({})...',(u_input,))) print(raw_tip) if u_input == 1: @@ -435,55 +435,55 @@ def bt_cli(): os.system("/etc/init.d/bt reload") elif u_input == 5: if sys.version_info[0] == 2: - input_pwd = raw_input(public.GetMsg("INPUT_NEW_PASS")) + input_pwd = raw_input(public.get_msg_gettext('Pls enter new password: ')) else: - input_pwd = input(public.GetMsg("INPUT_NEW_PASS")) + input_pwd = input(public.get_msg_gettext('Pls enter new password: ')) set_panel_pwd(input_pwd.strip(),True) elif u_input == 6: if sys.version_info[0] == 2: - input_user = raw_input(public.GetMsg("INPUT_NEW_USER")) + input_user = raw_input(public.get_msg_gettext('Pls enter new username(>5 characters): ')) else: - input_user = input(public.GetMsg("INPUT_NEW_USER")) + input_user = input(public.get_msg_gettext('Pls enter new username(>5 characters): ')) set_panel_username(input_user.strip()) elif u_input == 7: if sys.version_info[0] == 2: - input_mysql = raw_input(public.GetMsg("INPUT_NEW_MYSQL_PASS")) + input_mysql = raw_input(public.get_msg_gettext('Pls enter new MySQL root password:')) else: - input_mysql = input(public.GetMsg("INPUT_NEW_MYSQL_PASS")) + input_mysql = input(public.get_msg_gettext('Pls enter new MySQL root password:')) if not input_mysql: - print(public.GetMsg("PASS_NOT_EMPTY")) - return; + print(public.get_msg_gettext('|-ERROR, password cannot be empty')) + return if len(input_mysql) < 8: - print(public.GetMsg("PASS_LEN_ERR")) - return; + print(public.get_msg_gettext('|-ERROR, password cannot be less than 8 characters')) + return import re rep = "^[\w@\._]+$" if not re.match(rep, input_mysql): - print(public.GetMsg("PASS_SPECIAL_CHARACTRES_ERR")) - return; + print(public.get_msg_gettext('|-ERROR, password cannot contain special characters')) + return print(input_mysql) set_mysql_root(input_mysql.strip()) elif u_input == 8: - input_port = input(public.GetMsg("INPUT_NEW_PANEL_PORT")) + input_port = input(public.get_msg_gettext('Pls enter new panel port: ')) if sys.version_info[0] == 3: input_port = int(input_port) if not input_port: - print(public.GetMsg("INPUT_PANEL_PORT_ERR")) - return; + print(public.get_msg_gettext('|-ERROR, no valid port entered')) + return if input_port in [80,443,21,20,22]: - print(public.GetMsg("CANT_USE_USUALLY_PORT_ERR")) - return; + print(public.get_msg_gettext('|-ERROR, pls do NOT use the common port as panel port')) + return old_port = int(public.readFile('data/port.pl')) if old_port == input_port: - print(public.GetMsg("NEW_PORT_SAMEAS_OLD")) - return; + print(public.get_msg_gettext('|-ERROR, new port is the same as current panel port, no need to change')) + return is_exists = public.ExecShell("lsof -i:%s" % input_port) if len(is_exists[0]) > 5: - print(public.GetMsg("PORT_ALREADY_IN_USE")) - return; + print(public.get_msg_gettext('|-ERROR, specified port is already in use')) + return public.writeFile('data/port.pl',str(input_port)) if os.path.exists("/usr/bin/firewall-cmd"): @@ -507,17 +507,17 @@ def bt_cli(): auth_file = 'data/admin_path.pl' if os.path.exists(auth_file): os.remove(auth_file) os.system("/etc/init.d/bt reload") - print(public.GetMsg("CHANGE_LIMITED_CANCEL")) + print(public.get_msg_gettext('|-Entrance limit cancelled')) elif u_input == 12: auth_file = 'data/domain.conf' if os.path.exists(auth_file): os.remove(auth_file) os.system("/etc/init.d/bt reload") - print(public.GetMsg("CHANGE_DOMAIN_CANCEL")) + print(public.get_msg_gettext('|-Domain limit cancelled')) elif u_input == 13: auth_file = 'data/limitip.conf' if os.path.exists(auth_file): os.remove(auth_file) os.system("/etc/init.d/bt reload") - print(public.GetMsg("CHANGE_IP_CANCEL")) + print(public.get_msg_gettext('|-IP access limit cancelled')) elif u_input == 14: os.system("/etc/init.d/bt default") elif u_input == 15: diff --git a/class/userlogin.py b/class/userlogin.py index 8d05ced4..740288fa 100644 --- a/class/userlogin.py +++ b/class/userlogin.py @@ -12,43 +12,74 @@ from BTPanel import session,cache,json_header from flask import request,redirect,g class userlogin: - + limit_expire_time = 0 def request_post(self,post): if not hasattr(post, 'username') or not hasattr(post, 'password'): - return public.returnJson(False,'LOGIN_USER_EMPTY'),json_header + return public.returnJson(False,'User name or password cannot be empty!'),json_header self.error_num(False) - if self.limit_address('?') < 1: return public.returnJson(False,'LOGIN_ERR_LIMIT'),json_header + if self.limit_address('?') < 1: return public.returnJson(False,'You have failed to log in many times。 Please wait for {} seconds and try again!'.format(int(self.limit_expire_time - time.time()))),json_header + # if self.limit_address('?') < 1: return public.returnJson(False,'You cannot login now because login failed too many times!'),json_header post.username = post.username.strip() + # 核验用户名密码格式 + if len(post.username) != 32: return public.return_msg_gettext(False,'Disk inode has been exhausted, the panel has attempted to release the inode. Please try again ...'),json_header + if len(post.password) != 32: return public.return_msg_gettext(False,'Disk inode has been exhausted, the panel has attempted to release the inode. Please try again ...'),json_header + if not re.match(r"^\w+$",post.username): return public.return_msg_gettext(False,'Disk inode has been exhausted, the panel has attempted to release the inode. Please try again ...'),json_header + if not re.match(r"^\w+$",post.password): return public.return_msg_gettext(False,'Disk inode has been exhausted, the panel has attempted to release the inode. Please try again ...'),json_header + last_login_token = session.get('last_login_token',None) + if not last_login_token: + public.write_log_gettext('Login','Verification code error, account number: {}, verification code: {}, login IP: {}',('****','****',public.GetClientIp())) + return public.returnJson(False,"Verification failed, please refresh the page and log in again!"),json_header + public.chdck_salt() sql = db.Sql() - user_list = sql.table('users').field('id,username,password,salt').select() userInfo = None - for u_info in user_list: - if public.md5(u_info['username']) == post.username: - userInfo = u_info + user_plugin_file = '{}/users_main.py'.format(public.get_plugin_path('users')) + if os.path.exists(user_plugin_file): + user_list = sql.table('users').field('id,username,password,salt').select() + for u_info in user_list: + if public.md5(public.md5(u_info['username'] + last_login_token)) == post.username: + userInfo = u_info + else: + userInfo = sql.table('users').where('id=?',1).field('id,username,password,salt').find() + + if 'code' in session: if session['code'] and not 'is_verify_password' in session: if not hasattr(post, 'code'): return public.returnJson(False,'Verification code can not be empty!'),json_header + if not re.match(r"^\w+$",post.code): return public.returnJson(False,'Verification code is incorrect, please try again!'),json_header if not public.checkCode(post.code): - public.WriteLog('TYPE_LOGIN','LOGIN_ERR_CODE',('****','****',public.GetClientIp())) - return public.returnJson(False,'CODE_ERR'),json_header + public.write_log_gettext('Login','Verification code is incorrect, Username:{}, Verification Code:{}, Login IP:{}',('****','****',public.GetClientIp())) + return public.returnJson(False,'Verification code is incorrect, please try again!'),json_header try: - if not userInfo['salt']: + if not userInfo: + public.write_log_gettext('Login','Wrong password, account: {}, password: {}, login IP: {}',('****','******',public.GetClientIp())) + num = self.limit_address('+') + if not num: return public.returnJson(False,'You have failed to log in many times, please wait {} seconds and try again!'.format(int(self.limit_expire_time - time.time()))),json_header + return public.returnJson(False,'Username or password is wrong, Please refresh the page and try again, you can try again [{}] times'.format(num)),json_header + + if userInfo and not userInfo['salt']: public.chdck_salt() userInfo = sql.table('users').where('id=?',(userInfo['id'],)).field('id,username,password,salt').find() password = public.md5(post.password.strip() + userInfo['salt']) - if public.md5(userInfo['username']) != post.username or userInfo['password'] != password: - public.WriteLog('TYPE_LOGIN','LOGIN_ERR_PASS',('****','******',public.GetClientIp())) + s_username = public.md5(public.md5(userInfo['username'] + last_login_token)) + if s_username != post.username or userInfo['password'] != password: + public.write_log_gettext('Login','Password is incorrect, Username:{}, Password:{}, Login IP:{}',('****','******',public.GetClientIp())) num = self.limit_address('+') - return public.returnJson(False,'LOGIN_USER_ERR',(str(num),)),json_header + if not num: return public.returnJson(False,'You have failed to log in many times, please wait {} seconds and try again!'.format(int(self.limit_expire_time - time.time()))),json_header + return public.returnJson(False,'Invalid username or password. You have [{}] times left to try!',(str(num),)),json_header _key_file = "/www/server/panel/data/two_step_auth.txt" - #登陆告警 - public.run_thread(public.login_send_body,("Userinfo",userInfo['username'],public.GetClientIp(),str(request.environ.get('REMOTE_PORT')))) + + # 密码过期检测 + if sys.path[0] != 'class/': sys.path.insert(0,'class/') + if not public.password_expire_check(): + session['password_expire'] = True + # public.login_send_body("Userinfo",userInfo['username'],public.GetClientIp(),str(request.environ.get('REMOTE_PORT'))) if hasattr(post,'vcode'): + if not re.match(r"^\d+$",post.vcode): return public.returnJson(False,'Incorrect format of verification code'),json_header if self.limit_address('?',v="vcode") < 1: return public.returnJson(False,'You have failed verification many times, forbidden for 10 minutes'),json_header import pyotp secret_key = public.readFile(_key_file) @@ -62,6 +93,7 @@ class userlogin: 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.run_thread(public.login_send_body,("account",userInfo['username'],public.GetClientIp(),str(int(request.environ.get('REMOTE_PORT'))))) public.writeFile("/www/server/panel/data/dont_vcode_ip.txt",json.dumps({"client_ip":public.GetClientIp(),"add_time":now})) self.limit_address('--',v="vcode") self.set_cdn_host(post) @@ -70,6 +102,7 @@ class userlogin: acc_client_ip = self.check_two_step_auth() if not os.path.exists(_key_file) or acc_client_ip: + # public.run_thread(public.login_send_body,("account",userInfo['username'],public.GetClientIp(),str(int(request.environ.get('REMOTE_PORT'))))) self.set_cdn_host(post) return self._set_login_session(userInfo) self.limit_address('-') @@ -82,28 +115,30 @@ class userlogin: public.ExecShell("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())) + public.write_log_gettext('Login','Password is incorrect, Username:{}, Password:{}, Login IP:{}',('****','******',public.GetClientIp())) num = self.limit_address('+') - return public.returnJson(False,'LOGIN_USER_ERR',(str(num),)),json_header + if not num: return public.returnJson(False,'You have failed to log in many times, please wait {} seconds and try again!'.format(int(self.limit_expire_time - time.time()))),json_header + return public.returnJson(False,'Invalid username or password. You have [{}] times left to try!',(str(num),)),json_header def request_tmp(self,get): try: - if not hasattr(get,'tmp_token'): return public.returnJson(False,'INIT_ARGS_ERR'),json_header + if not hasattr(get,'tmp_token'): return public.returnJson(False,'Parameter ERROR!'),json_header if len(get.tmp_token) == 48: return self.request_temp(get) - if len(get.tmp_token) != 64: return public.returnJson(False,'INIT_ARGS_ERR'),json_header - if not re.match(r"^\w+$",get.tmp_token):return public.returnJson(False,'INIT_ARGS_ERR'),json_header + if len(get.tmp_token) != 64: return public.returnJson(False,'Parameter ERROR!'),json_header + if not re.match(r"^\w+$",get.tmp_token):return public.returnJson(False,'Parameter ERROR!'),json_header save_path = '/www/server/panel/config/api.json' data = json.loads(public.ReadFile(save_path)) - if not 'tmp_token' in data or not 'tmp_time' in data: return public.returnJson(False,'VERIFICATION_FAILED'),json_header - if (time.time() - data['tmp_time']) > 120: return public.returnJson(False,'EXPIRED_TOKEN'),json_header - if get.tmp_token != data['tmp_token']: return public.returnJson(False,'INIT_TOKEN_ERR'),json_header + if not 'tmp_token' in data or not 'tmp_time' in data: return public.returnJson(False,'Verification failed'),json_header + if (time.time() - data['tmp_time']) > 120: return public.returnJson(False,'Expired Token'),json_header + if get.tmp_token != data['tmp_token']: return public.returnJson(False,'Invalid Token!'),json_header userInfo = public.M('users').where("id=?",(1,)).field('id,username').find() session['login'] = True session['username'] = userInfo['username'] session['tmp_login'] = True session['uid'] = userInfo['id'] - public.WriteLog('TYPE_LOGIN','LOGIN_SUCCESS',(userInfo['username'],public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT')))) + ids = public.write_log_gettext('Login','Login success',(userInfo['username'],public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT')))) + public.cache_set(public.GetClientIp() + ":" + str(request.environ.get('REMOTE_PORT')), ids) self.limit_address('-') cache.delete('panelNum') cache.delete('dologin') @@ -121,28 +156,32 @@ class userlogin: def request_temp(self,get): try: - if len(get.__dict__.keys()) > 2: return public.getMsg('INIT_ARGS_ERR') - if not hasattr(get,'tmp_token'): return public.getMsg('INIT_ARGS_ERR') - if len(get.tmp_token) != 48: return public.getMsg('INIT_ARGS_ERR') - if not re.match(r"^\w+$",get.tmp_token):return public.getMsg('INIT_ARGS_ERR') + if len(get.__dict__.keys()) > 2: return public.get_msg_gettext('Parameter ERROR!') + if not hasattr(get,'tmp_token'): return public.get_msg_gettext('Parameter ERROR!') + if len(get.tmp_token) != 48: return public.get_msg_gettext('Parameter ERROR!') + if not re.match(r"^\w+$",get.tmp_token):return public.get_msg_gettext('Parameter ERROR!') skey = public.GetClientIp() + '_temp_login' - if not public.get_error_num(skey,10): return public.getMsg('AUTH_FAILED') + if not public.get_error_num(skey,10): return public.get_msg_gettext('10 consecutive authentication failures are prohibited for 1 hour') s_time = int(time.time()) + if public.M('temp_login').where('state=? and expire>?',(0,s_time)).field('id,token,salt,expire').count()==0: + public.set_error_num(skey) + return public.get_msg_gettext('Verification failed') + data = public.M('temp_login').where('state=? and expire>?',(0,s_time)).field('id,token,salt,expire').find() if not data: public.set_error_num(skey) - return public.getMsg('VERIFICATION_FAILED') + return public.get_msg_gettext('Verification failed') if not isinstance(data,dict): public.set_error_num(skey) - return public.getMsg('VERIFICATION_FAILED') + return public.get_msg_gettext('Verification failed') r_token = public.md5(get.tmp_token + data['salt']) if r_token != data['token']: public.set_error_num(skey) - return public.getMsg('VERIFICATION_FAILED') + return public.get_msg_gettext('Verification failed') public.set_error_num(skey,True) userInfo = public.M('users').where("id=?",(1,)).field('id,username').find() session['login'] = True - session['username'] = public.getMsg('TEMPORARY_ID',(data['id'],)) + session['username'] = public.get_msg_gettext('TEMPORARY_ID',(data['id'],)) session['tmp_login'] = True session['tmp_login_id'] = str(data['id']) session['tmp_login_expire'] = time.time() + 3600 @@ -152,7 +191,8 @@ class userlogin: os.makedirs(sess_path,384) public.writeFile(sess_path + '/' + str(data['id']),'') login_addr = public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT')) - public.WriteLog('TYPE_LOGIN','LOGIN_SUCCESS',(userInfo['username'],login_addr)) + ids = public.write_log_gettext('Login','Login succeed, Username: {}, Login IP: {}',(userInfo['username'],login_addr)) + public.cache_set(public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT')),ids) public.M('temp_login').where('id=?',(data['id'],)).update({"login_time":s_time,'state':1,'login_addr':login_addr}) self.limit_address('-') cache.delete('panelNum') @@ -161,10 +201,10 @@ class userlogin: self.set_request_token() self.login_token() self.set_cdn_host(get) - public.login_send_body("Temporary authorization",userInfo['username'],public.GetClientIp(),str(request.environ.get('REMOTE_PORT'))) + public.run_thread(public.login_send_body("Temporary authorization",userInfo['username'],public.GetClientIp(),str(request.environ.get('REMOTE_PORT')))) return redirect('/') except: - return public.getMsg('LOGIN_FAIL') + return public.get_msg_gettext('Login failed') def login_token(self): @@ -172,40 +212,40 @@ class userlogin: config.config().reload_session() def request_get(self,get): - #if os.path.exists('/www/server/panel/install.pl'): raise redirect('/install'); + ''' + @name 验证登录页面请求权限 + @author hwliang + @return False | Response + ''' + # 获取标题 if not 'title' in session: session['title'] = public.getMsg('NAME') - domain = public.readFile('data/domain.conf') - - if domain: - if(public.GetHost().lower() != domain.strip().lower()): - return 404 - # errorStr = public.ReadFile('./BTPanel/templates/' + public.GetConfigValue('template') + '/error2.html') - # try: - # errorStr = errorStr.format(public.getMsg('PAGE_ERR_TITLE'),public.getMsg('PAGE_ERR_DOMAIN_H1'),public.getMsg('PAGE_ERR_DOMAIN_P1'),public.getMsg('PAGE_ERR_DOMAIN_P2'),public.getMsg('PAGE_ERR_DOMAIN_P3'),public.getMsg('NAME'),public.getMsg('PAGE_ERR_HELP')) - # except IndexError:pass - # return errorStr - 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(','): - errorStr = public.ReadFile('./BTPanel/templates/' + public.GetConfigValue('template') + '/error2.html') - try: - errorStr = errorStr.format(public.getMsg('PAGE_ERR_TITLE'),public.getMsg('PAGE_ERR_IP_H1'),public.getMsg('PAGE_ERR_IP_P1',(public.GetClientIp(),)),public.getMsg('PAGE_ERR_IP_P2'),public.getMsg('PAGE_ERR_IP_P3'),public.getMsg('NAME'),public.getMsg('PAGE_ERR_HELP')) - except IndexError:pass - return errorStr + # 验证是否使用限制的域名访问 + domain_check = public.check_domain_panel() + if domain_check: return domain_check + + # 验证是否使用限制的IP地址访问 + ip_check = public.check_ip_panel() + if ip_check: return ip_check + + # 验证是否已经登录 if 'login' in session: if session['login'] == True: return redirect('/') + # 复位验证码 if not 'code' in session: session['code'] = False + + # 记录错误次数 self.error_num(False) #生成request_token def set_request_token(self): - session['request_token_head'] = public.GetRandomString(48) + html_token_key = public.get_csrf_html_token_key() + session[html_token_key] = public.GetRandomString(48) + session[html_token_key.replace("https_","")] = public.GetRandomString(48) + def set_cdn_host(self,get): try: @@ -227,21 +267,22 @@ class userlogin: num = 1 if s: cache.inc(nKey,1) if num > 6: session['code'] = True - + #IP限制 def limit_address(self,type,v=""): - import time clientIp = public.GetClientIp() numKey = 'limitIpNum_' + v + clientIp - limit = 6 - outTime = 600 + limit = 5 + outTime = 300 try: #初始化 num1 = cache.get(numKey) if not num1: - cache.set(numKey,1,outTime) - num1 = 1 - + cache.set(numKey,0,outTime) + num1 = 0 + + self.limit_expire_time = cache.get_expire_time(numKey) + #计数 if type == '+': cache.inc(numKey,1) @@ -278,27 +319,36 @@ class userlogin: session['username'] = userInfo['username'] session['uid'] = userInfo['id'] session['login_user_agent'] = public.md5(request.headers.get('User-Agent','')) - public.WriteLog('TYPE_LOGIN','LOGIN_SUCCESS',(userInfo['username'],public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT')))) + ids = public.write_log_gettext('Login','Login succeed, Username: {}, Login IP: {}',(userInfo['username'],public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT')))) + public.cache_set(public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT')),ids) self.limit_address('-') cache.delete('panelNum') cache.delete('dologin') session['session_timeout'] = time.time() + public.get_session_timeout() + if 'last_login_token' in session: del(session['last_login_token']) self.set_request_token() self.login_token() login_type = 'data/app_login.pl' if os.path.exists(login_type): os.remove(login_type) - return public.returnJson(True,'LOGIN_SUCCESS'),json_header + try: + default_pl = "{}/default.pl".format(public.get_panel_path()) + public.writeFile(default_pl,"********") + public.run_thread(public.login_send_body, ( + "Userinfo", userInfo['username'], public.GetClientIp(), str(request.environ.get('REMOTE_PORT')))) + except: + pass + return public.returnJson(True,'Login succeeded, loading...'),json_header except Exception as ex: stringEx = str(ex) if stringEx.find('unsupported') != -1 or stringEx.find('-1') != -1: public.ExecShell("rm -f /tmp/sess_*") public.ExecShell("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())) + return public.returnJson(False,'Disk inode has been exhausted, the panel has attempted to release the inode. Please try again ...'),json_header + public.write_log_gettext('Login','Password is incorrect, Username:{}, Password:{}, Login IP:{}',('****','******',public.GetClientIp())) num = self.limit_address('+') - return public.returnJson(False,'LOGIN_USER_ERR',(str(num),)),json_header + return public.returnJson(False,'Invalid username or password. You have [{}] times left to try!',(str(num),)),json_header # 检查是否需要进行二次验证 diff --git a/class/userlogin.py.baiduyun.uploading.cfg b/class/userlogin.py.baiduyun.uploading.cfg deleted file mode 100644 index 195c60de444b85c9ab7d50ac7a659f0bee892fab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3034 zcmXBWNmuGx5Cz~f=C3#qfr~Pvkxpz75pgDnARt`Si^#8kU*)Z=KvK=#yXsU<~?L28KN|n@O{Son* zE@bS}7o3lD&3Z5HmF*^VAy@h5=`=OcCas~Z(jRy4A|5f6Qx5Y&%!B)u=oOR++cJ&Q zfu={kM!I!w#oYVdifN}4nx9ff##}^GHBaeTRUP>&y2|MVQ_c02|9pnEqv^o2V_l_h zoyd!><6o!q^jCC?o(H}<#;9O{ai1>vejF#dQs()f{sZn4m9Ko~`uMCP-~PYpX*i;$ z7txPUzf`(+KIq7;y5@LmuD{Yzjn2OyP@Z( z>NeB{|JZQ@s{?tc;NiC9zD?jGu8q;6im73r6 z`T}beY8k>obWvoF#y8A8%z2T2uslOq>EI28g{&(Tdh)nlAiVKDySi5G2JSW7G0U0Q zK%J;=i{}=`M)a4yz4OfcM@@Muzx`I-Rm8`H{6B9qwmr@P6_4~D)wzRHl3h^wQheA( zI-kRPim5}%h_8jet}8uJ4fU0>HL2)-SLd6oC7f4kKZCY{_vE+bEWzwMN7-sWP*A3M z38M)+)b6qx^X@L_T~oO>k9)ZX%wxHGdSd!|re$SCFI7;aeB$Y8%UkI{&x&_gN-U?& z7TgK0p~wU83C%7Q^OYO z((jPw*wN0MYnGn>PyV{ROO>3__UhQWKcjoBl2G<4D>UEue^5h%!stLweC#*M>Eu42dDsAV8)M*j!y z;EEM0s_w(@ui3eT)^%5aT@c$bkC*!JE&CnDJ>SCpl`d?s3?b&!HGmyYVO`Fxo;>5M zTrTT-{0CrHw6~wUNF*WV_gr{Q~N_HCtqAF0mgoB~83# z*)!33M@&=9e^pI8`CavwWS`@R*d~AMMZb`ydU?g~kGzB6>pWwjiC z;61^f;0Qex8L|8Cvl`Wh!{jpzNIqN&a{^`Mn? zEZ&OeuHs#LshXIy-%MppWxUf{@-HAZvmP%+M;~*lUr-kN<(>a{*9;p18?k=bts?aW1*`h%Hu00-jC6(Q{E~)@g4M;t;4&hs_U7^Tb2&=k?10 zirqSzUv*dH#vT<<&$;=it0le~##je9 za@jidd859OoI1Rfm@TXy9a&k`*t=_DHvFL`roFGiq59sTpX2H4PR$W->UaKFgKs%& zRYgTtVv2B{FhqA^>f>2z^UZWL_MII)4-l^0&8jus*Uy*%aRpY%D2&-%j z 60: - return public.returnMsg(False, 'QRCORE_EXPIRE') - session_id = public.get_session_id() - if cache.get(session_id) == 'True': - return public.returnMsg(True, 'Scan QRCORE successfully') + try: + key, init_time, tid, status = public.readFile(self.app_path+'app_login_check.pl').split(':') + if time.time() - float(init_time) > 60: + return public.returnMsg(False, 'QR code expired') + session_id = public.get_session_id() + if cache.get(session_id) == public.md5(uuid.UUID(int=uuid.getnode()).hex): + return public.returnMsg(True, 'Scan QRCORE successfully') + except: + os.remove(self.app_path + "app_login_check.pl") + return public.returnMsg(False, '') return public.returnMsg(False, '') # 返回二维码地址 def login_qrcode(self, get): - tid = public.GetRandomString(12) + tid = public.GetRandomString(32) qrcode_str = 'https://app.bt.cn/app.html?&panel_url='+public.getPanelAddr()+'&v=' + public.GetRandomString(3)+'?login&tid=' + tid - data = public.get_session_id() + ':' + str(time.time()) + data = public.get_session_id() + ':' + str(time.time()) + ':' + tid + ':' + tid public.writeFile(self.app_path + "app_login_check.pl", data) cache.set(tid,public.get_session_id(),360) cache.set(public.get_session_id(),tid,360) @@ -50,15 +55,20 @@ class wxapp(): # 设置登录状态 def set_login(self, get): session_id = public.get_session_id() - if cache.get(session_id) == 'True': - return self.check_app_login(get) + if cache.get(session_id): + if cache.get(session_id) == public.md5(uuid.UUID(int=uuid.getnode()).hex): + return self.check_app_login(get) + else: + cache.delete(cache.get(session_id)) + cache.delete(session_id) + return public.returnMsg(False, 'Login failed 2') return public.returnMsg(False, 'Login failed 1') #验证APP是否登录成功 def check_app_login(self,get): #判断是否存在绑定 btapp_info = json.loads(public.readFile('/www/server/panel/config/api.json')) - if not btapp_info:return public.returnMsg(False,'Unbound') + if not btapp_info:return public.returnMsg(False,'Unbound!') if not btapp_info['open']:return public.returnMsg(False,'API is not turned on') if not 'apps' in btapp_info:return public.returnMsg(False,'Unbound phone') if not btapp_info['apps']:return public.returnMsg(False,'Unbound phone') @@ -67,19 +77,22 @@ class wxapp(): if not os.path.exists(self.app_path+'app_login_check.pl'):return public.returnMsg(False,'Waiting for APP scan code login 1') data = public.readFile(self.app_path+'app_login_check.pl') public.ExecShell('rm ' + self.app_path+"app_login_check.pl") - secret_key, init_time = data.split(':') + secret_key, init_time, tid, status = data.split(':') if len(session_id)!=64:return public.returnMsg(False,'Waiting for APP scan code login 2') if len(secret_key)!=64:return public.returnMsg(False,'Waiting for APP scan code login 2') + if session_id != secret_key: + return public.returnMsg(False,'QR code expired') if time.time() - float(init_time) > 60: return public.returnMsg(False,'Waiting for APP scan code login') - if session_id != secret_key: - return public.returnMsg(False,'Waiting for APP scan code login') + import uuid + if status != uuid.UUID(int=uuid.getnode()).hex[-12:]: return public.returnMsg(False, '当前二维码失效222') cache.delete(session_id) + cache.delete(tid) userInfo = public.M('users').where("id=?",(1,)).field('id,username').find() session['login'] = True session['username'] = userInfo['username'] session['tmp_login'] = True - public.WriteLog('TYPE_LOGIN','APP scan code login, account: {}, login IP: {}'.format(userInfo['username'],public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT')))) + public.WriteLog('Login','APP scan code login, account: {}, login IP: {}'.format(userInfo['username'],public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT')))) cache.delete('panelNum') cache.delete('dologin') session['session_timeout'] = time.time() + public.get_session_timeout() diff --git a/config/menu.json b/config/menu.json index 8959d033..c5b85bf5 100644 --- a/config/menu.json +++ b/config/menu.json @@ -26,41 +26,55 @@ "class": "menu_data", "id": "memuAdatabase", "sort": 4 + }, + { + "title": "Docker", + "href": "/docker", + "class": "menu_docker", + "id": "memuDocker", + "sort": 5 }, { "title": "Monitor", "href": "/control", "class": "menu_control", "id": "memuAcontrol", - "sort": 5 + "sort": 6 }, { "title": "Security", "href": "/firewall", "class": "menu_firewall", "id": "memuAfirewall", - "sort": 6 + "sort": 7 + }, + { + "title": "WAF", + "href": "/btwaf/index", + "class": "menu_btwaf", + "id": "memu_btwaf", + "sort": 8 }, { "title": "Files", "href": "/files", "class": "menu_folder", "id": "memuAfiles", - "sort": 7 + "sort": 9 }, { "title": "Terminal", "href": "/xterm", "class": "menu_xterm", "id": "memuAxterm", - "sort": 8 + "sort": 10 }, { "title": "Cron", "href": "/crontab", "class": "menu_day", "id": "memuAcrontab", - "sort": 9 + "sort": 11 }, { @@ -68,20 +82,20 @@ "href": "/soft", "class": "menu_soft", "id": "memuAsoft", - "sort": 10 + "sort": 12 }, { "title": "Settings", "href": "/config", "class": "menu_set", "id": "memuAconfig", - "sort": 11 + "sort": 13 }, { "title": "Log out", "href": "/login?dologin=True", "class": "menu_exit", "id": "dologin", - "sort": 12 + "sort": 14 } ] \ No newline at end of file diff --git a/data/default.sql b/data/default.sql deleted file mode 100644 index 42de4208..00000000 --- a/data/default.sql +++ /dev/null @@ -1,131 +0,0 @@ - -CREATE TABLE IF NOT EXISTS `backup` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `type` INTEGER, - `name` TEXT, - `pid` INTEGER, - `filename` TEXT, - `size` INTEGER, - `addtime` TEXT -); - -CREATE TABLE IF NOT EXISTS `binding` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `pid` INTEGER, - `domain` TEXT, - `path` TEXT, - `port` INTEGER, - `addtime` TEXT -); - - -CREATE TABLE IF NOT EXISTS `config` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `webserver` TEXT, - `backup_path` TEXT, - `sites_path` TEXT, - `status` INTEGER, - `mysql_root` TEXT -); - -INSERT INTO `config` (`id`, `webserver`, `backup_path`, `sites_path`, `status`, `mysql_root`) VALUES -(1, 'nginx', '/www/backup', '/www/wwwroot', 0, 'admin'); - - -CREATE TABLE IF NOT EXISTS `crontab` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `name` TEXT, - `type` TEXT, - `where1` TEXT, - `where_hour` INTEGER, - `where_minute` INTEGER, - `echo` TEXT, - `addtime` TEXT -); - -CREATE TABLE IF NOT EXISTS `databases` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `pid` INTEGER, - `name` TEXT, - `username` TEXT, - `password` TEXT, - `accept` TEXT, - `ps` TEXT, - `addtime` TEXT -); - -CREATE TABLE IF NOT EXISTS `firewall` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `port` TEXT, - `ps` TEXT, - `addtime` TEXT -); - -INSERT INTO `firewall` (`id`, `port`, `ps`, `addtime`) VALUES -(2, '80', '网站默认端口', '0000-00-00 00:00:00'), -(3, '8888', 'WEB面板', '0000-00-00 00:00:00'), -(4, '21', 'FTP服务', '0000-00-00 00:00:00'), -(5, '22', 'SSH远程管理服务', '0000-00-00 00:00:00'); - - -CREATE TABLE IF NOT EXISTS `ftps` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `pid` INTEGER, - `name` TEXT, - `password` TEXT, - `path` TEXT, - `status` TEXT, - `ps` TEXT, - `addtime` TEXT -); - - -CREATE TABLE IF NOT EXISTS `logs` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `type` TEXT, - `log` TEXT, - `addtime` TEXT -); - -CREATE TABLE IF NOT EXISTS `sites` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `name` TEXT, - `path` TEXT, - `status` TEXT, - `index` TEXT, - `ps` TEXT, - `addtime` TEXT -); - -CREATE TABLE IF NOT EXISTS `domain` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `pid` INTEGER, - `name` TEXT, - `port` INTEGER, - `addtime` TEXT -); - -CREATE TABLE IF NOT EXISTS `users` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `username` TEXT, - `password` TEXT, - `login_ip` TEXT, - `login_time` TEXT, - `phone` TEXT, - `email` TEXT -); - -INSERT INTO `users` (`id`, `username`, `password`, `login_ip`, `login_time`, `phone`, `email`) VALUES -(1, 'admin', '21232f297a57a5a743894a0e4a801fc3', '192.168.0.10', '2016-12-10 15:12:56', 0, '287962566@qq.com'); - - -CREATE TABLE IF NOT EXISTS `tasks` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `name` TEXT, - `type` TEXT, - `status` TEXT, - `addtime` TEXT, - `start` INTEGER, - `end` INTEGER, - `execstr` TEXT -); diff --git a/data/libList.conf b/data/libList.conf index 382f70ba..99226d05 100644 --- a/data/libList.conf +++ b/data/libList.conf @@ -1,5 +1,5 @@ [{ - "name":"FTP storage", + "name":"FTP Storage", "type":"Cron job", "ps":"Package the website or database back to the FTP storage space.", "status":false, diff --git a/data/node.json b/data/node.json index 756e86f2..b4a598e0 100644 --- a/data/node.json +++ b/data/node.json @@ -1,30 +1,18 @@ [ { - "protocol": "http://", + "protocol": "https://", "address": "node.aapanel.com", "port": "80", "ping": 500 }, { - "protocol": "http://", - "address": "128.1.164.196", - "port": "80", - "ping": 500 - }, - { - "protocol": "http://", - "address": "45.76.53.20", - "port": "80", - "ping": 500 - }, - { - "protocol": "http://", + "protocol": "https://", "address": "dg2.bt.cn", "port": "80", "ping": 500 }, { - "protocol": "http://", + "protocol": "https://", "address": "dg1.bt.cn", "port": "80", "ping": 500 diff --git a/data/o.pl b/data/o.pl deleted file mode 100644 index 8ba7e15f..00000000 --- a/data/o.pl +++ /dev/null @@ -1 +0,0 @@ -github \ No newline at end of file diff --git a/init.sh b/init.sh index 23ab81df..64302a2a 100644 --- a/init.sh +++ b/init.sh @@ -65,7 +65,6 @@ panel_start() get_panel_pids if [ "$isStart" == '' ];then rm -f $pidfile - panel_port_check echo -e "Starting Bt-Panel...\c" nohup $panel_path/BT-Panel >> $log_file 2>&1 & isStart="" @@ -81,6 +80,7 @@ panel_start() fi done if [ "$isStart" == '' ];then + panel_port_check echo -e "\033[31mfailed\033[0m" echo '------------------------------------------------------' tail -n 20 $log_file @@ -231,7 +231,6 @@ panel_reload() kill -9 $p done rm -f $pidfile - panel_port_check echo -e "Reload Bt-Panel.\c"; nohup $panel_path/BT-Panel >> $log_file 2>&1 & isStart="" @@ -247,6 +246,7 @@ panel_reload() fi done if [ "$isStart" == '' ];then + panel_port_check echo -e "\033[31mfailed\033[0m" echo '------------------------------------------------------' tail -n 20 $log_file @@ -331,7 +331,7 @@ case "$1" in echo -e "password: $password" echo -e "\033[33mWarning:\033[0m" echo -e "\033[33mIf you cannot access the panel, \033[0m" - echo -e "\033[33mrelease the following port (7800|888|80|443|20|21) in the security group\033[0m" + echo -e "\033[33mrelease the following port (8888|888|80|443|20|21) in the security group\033[0m" echo -e "==================================================================" ;; *) diff --git a/install/install_soft.sh b/install/install_soft.sh index e4c13ddc..1bb4dc98 100644 --- a/install/install_soft.sh +++ b/install/install_soft.sh @@ -1,24 +1,37 @@ #!/bin/bash PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH + +#CN='125.88.182.172' +#HK='download.bt.cn' +#HK2='103.224.251.67' +#US='128.1.164.196' +#sleep 0.5; +#CN_PING=`ping -c 1 -w 1 $CN|grep time=|awk '{print $7}'|sed "s/time=//"` +#HK_PING=`ping -c 1 -w 1 $HK|grep time=|awk '{print $7}'|sed "s/time=//"` +#HK2_PING=`ping -c 1 -w 1 $HK2|grep time=|awk '{print $7}'|sed "s/time=//"` +#US_PING=`ping -c 1 -w 1 $US|grep time=|awk '{print $7}'|sed "s/time=//"` + +#echo "$HK_PING $HK" > ping.pl +#echo "$HK2_PING $HK2" >> ping.pl +#echo "$US_PING $US" >> ping.pl +#echo "$CN_PING $CN" >> ping.pl +nodeAddr=`sort -V ping.pl|sed -n '1p'|awk '{print $2}'` +#if [ "$nodeAddr" == "" ];then +# nodeAddr=$HK2 +#fi +#serverUrl=http://$nodeAddr:5880/install +serverUrl=https://node.aapanel.com/install mtype=$1 actionType=$2 name=$3 version=$4 -. /www/server/panel/install/public.sh -serverUrl=$NODE_URL/install if [ ! -f 'lib.sh' ];then - wget -O lib.sh $serverUrl/$mtype/lib.sh + wget -O lib.sh $serverUrl/$mtype/lib.sh --no-check-certificate fi - -libNull=`cat lib.sh` -if [ "$libNull" == '' ];then - wget -O lib.sh $serverUrl/$mtype/lib.sh -fi - -wget -O $name.sh $serverUrl/$mtype/$name.sh +wget -O $name.sh $serverUrl/$mtype/$name.sh --no-check-certificate if [ "$actionType" == 'install' ];then - bash lib.sh + sh lib.sh fi -bash $name.sh $actionType $version +sh $name.sh $actionType $version diff --git a/install/public.sh b/install/public.sh index 584e5f1f..146a9f22 100644 --- a/install/public.sh +++ b/install/public.sh @@ -1,48 +1,58 @@ #!/bin/bash PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +pyenv_bin=/www/server/panel/pyenv/bin +rep_path=${pyenv_bin}:$PATH +if [ -d "$pyenv_bin" ];then + PATH=$rep_path +fi export PATH export LANG=en_US.UTF-8 export LANGUAGE=en_US:en get_node_url(){ - nodes=(http://183.235.223.101:3389 http://125.88.182.172:5880 http://128.1.164.196 http://103.224.251.67:5880 http://download.bt.cn); - i=1; - for node in ${nodes[@]}; - do - start=`date +%s.%N` - result=`curl -sS --connect-timeout 3 -m 60 $node/check.txt` - if [ "$result" = 'True' ];then - end=`date +%s.%N` - start_s=`echo $start | cut -d '.' -f 1` - start_ns=`echo $start | cut -d '.' -f 2` - end_s=`echo $end | cut -d '.' -f 1` - end_ns=`echo $end | cut -d '.' -f 2` - time_micro=$(( (10#$end_s-10#$start_s)*1000000 + (10#$end_ns/1000 - 10#$start_ns/1000) )) - time_ms=$(($time_micro/1000)) - values[$i]=$time_ms; - urls[$time_ms]=$node - i=$(($i+1)) - if [ $time_ms -lt 50 ];then - break; - fi - fi - done - j=5000 - for n in ${values[@]}; - do - if [ $j -gt $n ];then - j=$n - fi - if [ $j -lt 50 ];then - break; - fi - done - if [ $j = 5000 ];then - NODE_URL='http://download.bt.cn'; - else - NODE_URL=${urls[$j]} - fi - + NODE_URL='https://node.aapanel.com'; + #nodes=(http://dg2.bt.cn http://dg1.bt.cn http://36.133.1.8:5880 http://123.129.198.197 http://38.34.185.130 http://116.213.43.206:5880 http://128.1.164.196); + #tmp_file1=/dev/shm/net_test1.pl + #tmp_file2=/dev/shm/net_test2.pl + #[ -f "${tmp_file1}" ] && rm -f ${tmp_file1} + #[ -f "${tmp_file2}" ] && rm -f ${tmp_file2} + #touch $tmp_file1 + #touch $tmp_file2 + #for node in ${nodes[@]}; + #do + # NODE_CHECK=$(curl --connect-timeout 3 -m 3 2>/dev/null -w "%{http_code} %{time_total}" ${node}/net_test|xargs) + # RES=$(echo ${NODE_CHECK}|awk '{print $1}') + # NODE_STATUS=$(echo ${NODE_CHECK}|awk '{print $2}') + # TIME_TOTAL=$(echo ${NODE_CHECK}|awk '{print $3 * 1000 - 500 }'|cut -d '.' -f 1) + # if [ "${NODE_STATUS}" == "200" ];then + # if [ $TIME_TOTAL -lt 100 ];then + # if [ $RES -ge 1500 ];then + # echo "$RES $node" >> $tmp_file1 + # fi + # else + # if [ $RES -ge 1500 ];then + # echo "$TIME_TOTAL $node" >> $tmp_file2 + # fi + # fi +# +# i=$(($i+1)) +# if [ $TIME_TOTAL -lt 100 ];then +# if [ $RES -ge 3000 ];then +# break; +# fi +# fi +# fi +# done + +# NODE_URL=$(cat $tmp_file1|sort -r -g -t " " -k 1|head -n 1|awk '{print $2}') +# if [ -z "$NODE_URL" ];then +# NODE_URL=$(cat $tmp_file2|sort -g -t " " -k 1|head -n 1|awk '{print $2}') +# if [ -z "$NODE_URL" ];then +# NODE_URL='http://download.bt.cn'; +# fi +# fi +# rm -f $tmp_file1 +# rm -f $tmp_file2 } GetCpuStat(){ @@ -70,6 +80,51 @@ GetPackManager(){ PM="apt-get" fi } + +bt_check(){ + p_path=/www/server/panel/class/panelPlugin.py + if [ -f $p_path ];then + is_ext=$(cat $p_path|grep btwaf) + if [ "$is_ext" != "" ];then + send_check + fi + fi + + p_path=/www/server/panel/BTPanel/templates/default/index.html + if [ -f $p_path ];then + is_ext=$(cat $p_path|grep fbi) + if [ "$is_ext" != "" ];then + send_check + fi + fi +} + +send_check(){ + chattr -i /etc/init.d/bt + chmod +x /etc/init.d/bt + p_path2=/www/server/panel/class/common.py + p_version=$(cat $p_path2|grep "version = "|awk '{print $3}'|tr -cd [0-9.]) + curl -sS --connect-timeout 3 -m 60 http://www.bt.cn/api/panel/notpro?version=$p_version + NODE_URL="" + exit 0; +} +GetSysInfo(){ + if [ "${PM}" = "yum" ]; then + SYS_VERSION=$(cat /etc/redhat-release) + elif [ "${PM}" = "apt-get" ]; then + SYS_VERSION=$(cat /etc/issue) + fi + SYS_INFO=$(uname -msr) + SYS_BIT=$(getconf LONG_BIT) + MEM_TOTAL=$(free -m|grep Mem|awk '{print $2}') + CPU_INFO=$(getconf _NPROCESSORS_ONLN) + GCC_VER=$(gcc -v 2>&1|grep "gcc version"|awk '{print $3}') + CMAKE_VER=$(cmake --version|grep version|awk '{print $3}') + + echo -e ${SYS_VERSION} + echo -e Bit:${SYS_BIT} Mem:${MEM_TOTAL}M Core:${CPU_INFO} gcc:${GCC_VER} cmake:${CMAKE_VER} + echo -e ${SYS_INFO} +} cpuInfo=$(getconf _NPROCESSORS_ONLN) if [ "${cpuInfo}" -ge "4" ];then GetCpuStat @@ -77,7 +132,26 @@ else cpuCore="1" fi GetPackManager -if [ ! $NODE_URL ];then - echo 'Selecting download node...'; - get_node_url + +if [ -d "/www/server/phpmyadmin/pma" ];then + rm -rf /www/server/phpmyadmin/pma + EN_CHECK=$(cat /www/server/panel/config/config.json |grep English) + if [ "${EN_CHECK}" ];then + curl http://download.bt.cn/install/update6_en.sh|bash + else + curl http://download.bt.cn/install/update6.sh|bash + fi + echo > /www/server/panel/data/restart.pl fi + +if [ ! $NODE_URL ];then + EN_CHECK=$(cat /www/server/panel/config/config.json |grep English) + if [ -z "${EN_CHECK}" ];then + echo '正在选择下载节点...'; + else + echo "selecting download node..."; + fi + get_node_url + bt_check +fi + diff --git a/requirements.txt b/requirements.txt index dd833ab2..0302613c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,19 +1,54 @@ -Flask>=1.0.2 -paramiko>=2.6.0 -flask-socketio>=4.1.0 -python-socketio>=4.2.0 -flask_sockets>=0.2.1 -Werkzeug>=0.15.1 -Pillow==5.4.1 -requests>=2.20 -cffi>=1.12.3 -psutil>=5.2.0 -chardet>=3.0.4 -Flask-Session>=0.3.1 -flask-sqlalchemy>=2.3.2 -gunicorn>=18.0 -gevent-websocket>=0.10.1 -pyopenssl>=19.0 -cryptography>=2.7 -six>=1.12.0 -pyOpenSSL>=21.0.0 \ No newline at end of file +bcrypt==3.2.0 +certifi==2021.10.8 +cffi==1.15.0 +chardet==4.0.0 +charset-normalizer==2.0.12 +click==8.0.4 +configparser==5.2.0 +cos-python-sdk-v5==1.9.15 +crcmod==1.7 +cryptography==36.0.1 +Cython==0.29.28 +Deprecated==1.2.13 +dicttoxml==1.7.4 +dnspython==2.2.1 +docker==5.0.3 +Flask==2.0.3 +flask-sock==0.5.1 +h11==0.13.0 +idna==3.3 +IPy==1.1 +itsdangerous==2.1.1 +Jinja2==3.0.3 +MarkupSafe==2.1.0 +oauthlib==3.2.0 +packaging==21.3 +paramiko==2.10.2 +Pillow==9.0.1 +psutil==5.9.0 +pyasn1==0.4.8 +pycparser==2.21 +pycryptodome==3.14.1 +pyinotify==0.9.6 +PyMySQL==1.0.2 +PyNaCl==1.5.0 +pyOpenSSL==22.0.0 +pyparsing==3.0.7 +qiniu==7.5.0 +qrcode==7.3.1 +redis==4.1.4 +requests==2.27.1 +requests-file==1.5.1 +requests-oauthlib==1.3.1 +rsa==4.8 +simple-websocket==0.5.1 +six==1.16.0 +soupsieve==2.3.1 +upyun==2.5.5 +urllib3==1.26.8 +websocket-client==1.3.1 +Werkzeug==2.0.3 +wrapt==1.14.0 +wsproto==1.1.0 +gevent +pyyaml \ No newline at end of file diff --git a/runconfig.py b/runconfig.py index f46f1a8b..b5b1d579 100644 --- a/runconfig.py +++ b/runconfig.py @@ -5,7 +5,7 @@ f.close() if bt_port: bt_port.strip() else: - bt_port = 7800 + bt_port = 8888 bind = [] if os.path.exists('data/ipv6.pl'): bind.append('[0:0:0:0:0:0:0:0]:%s' % bt_port) diff --git a/script/ftp.sh b/script/ftp.sh index 30b0750d..34c9187e 100644 --- a/script/ftp.sh +++ b/script/ftp.sh @@ -5,16 +5,16 @@ install_tmp='/tmp/bt_install.pl' download_Url=http://download.bt.cn Install_Ftp() { - echo '正在安装脚本文件...' > $install_tmp + echo 'Installing script files...' > $install_tmp wget -O /www/server/panel/script/backup_ftp.py $download_Url/install/lib/script/backup_ftp.py -T 5 - echo '安装完成' > $install_tmp + echo 'The installation is complete' > $install_tmp } Uninstall_Ftp() { rm -f /www/server/panel/script/backup_ftp.py - echo '卸载完成' > $install_tmp + echo 'Uninstall complete' > $install_tmp } diff --git a/script/panel_ssl_task.py b/script/panel_ssl_task.py index 1b379e1f..eeb58b35 100644 --- a/script/panel_ssl_task.py +++ b/script/panel_ssl_task.py @@ -7,7 +7,8 @@ from json import loads lets_info = public.readFile("/www/server/panel/ssl/lets.info") if lets_info: lets_info = loads(lets_info) - if setPanelLets.setPanelLets().check_cert_update(lets_info['domain']): + res = setPanelLets.setPanelLets().check_cert_update(lets_info['domain']) + if res['status'] and res['msg'] == '1': strTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time()))) public.writeFile("/tmp/panelSSL.pl","{} Panel certificate updated successfully\n".format(strTime),"a+") public.writeFile('/www/server/panel/data/reload.pl',"1") \ No newline at end of file diff --git a/script/rememory.sh b/script/rememory.sh index 925e9dfc..2343eb22 100644 --- a/script/rememory.sh +++ b/script/rememory.sh @@ -6,7 +6,7 @@ export PATH #+------------------------------------ endDate=`date +"%Y-%m-%d %H:%M:%S"` -log="释放内存!" +log="Free up memory!" echo "★[$endDate] $log" echo '----------------------------------------------------------------------------' diff --git a/task.py b/task.py index 20cc843e..bfe15893 100644 --- a/task.py +++ b/task.py @@ -15,6 +15,9 @@ import sys import os import logging +import datetime +import threading + from json import dumps, loads from psutil import Process, pids, cpu_count, cpu_percent, net_io_counters, disk_io_counters, virtual_memory os.environ['BT_TASK'] = '1' @@ -98,7 +101,6 @@ def ExecShell(cmdstring, cwd=None, timeout=None, shell=True, symbol = '&>'): try: global logPath import shlex - import datetime import subprocess import time sub = subprocess.Popen(cmdstring+ symbol +logPath, cwd=cwd, @@ -425,12 +427,16 @@ def check502Task(): while True: check502() sess_expire() + mysql_quota_check() time.sleep(600) except Exception as ex: logging.info(ex) time.sleep(600) check502Task() +# MySQL配额检查 +def mysql_quota_check(): + os.system(get_python_bin() +" /www/server/panel/script/mysql_quota.py > /dev/null") # session过期处理 def sess_expire(): @@ -494,7 +500,7 @@ def panel_status(): def update_panel(): - os.system("curl http://download.bt.cn/install/update6_en.sh|bash &") + os.system("curl https://download.bt.cn/install/update6_en.sh|bash &") def service_panel(action='reload'): @@ -602,6 +608,23 @@ def check_files_panel(): public.writeFile(cf, i['body']) os.system("bash {}/init.sh reload &".format(base_path)) +def func(): + os.system(get_python_bin() + " {}/script/scan_log.py > /dev/null".format(base_path)) + #如果需要循环调用,就要添加以下方法 + timer = threading.Timer(86400, func) + timer.start() + +def scan_log_site(): + now_time = datetime.datetime.now() + next_time = now_time + datetime.timedelta(days=+1) + next_year = next_time.date().year + next_month = next_time.date().month + next_day = next_time.date().day + next_time = datetime.datetime.strptime(str(next_year) + "-" + str(next_month) + "-" + str(next_day) + " 03:00:00", + "%Y-%m-%d %H:%M:%S") + timer_start_time = (next_time - now_time).total_seconds() + timer = threading.Timer(timer_start_time, func) + timer.start() # 面板消息提醒 # def check_panel_msg(): @@ -634,7 +657,6 @@ def main(): datefmt='%Y-%m-%d %H:%M:%S', filename=task_log_file, filemode='a+') logging.info('Service started') - import threading t = threading.Thread(target=systemTask) t.setDaemon(True) t.start() @@ -672,6 +694,9 @@ def main(): # p = threading.Thread(target=check_panel_msg) # p.setDaemon(True) # p.start() + p = threading.Thread(target=scan_log_site) + p.setDaemon(True) + p.start() startTask() diff --git a/tools.py b/tools.py index a7485d1f..032a1cbd 100644 --- a/tools.py +++ b/tools.py @@ -190,31 +190,64 @@ def CloseTask(): os.system('/etc/init.d/bt restart') print(public.GetMsg("CLEAR_TASK",(int(ncount),))) +def get_ipaddress(): + ''' + @name 获取本机IP地址 + @author hwliang<2020-11-24> + @return list + ''' + ipa_tmp = public.ExecShell("ip a |grep inet|grep -v inet6|grep -v 127.0.0.1|awk '{print $2}'|sed 's#/[0-9]*##g'")[0].strip() + iplist = ipa_tmp.split('\n') + return iplist +def get_host_all(): + local_ip = ['127.0.0.1','::1','localhost'] + ip_list = [] + bind_ip = get_ipaddress() + + for ip in bind_ip: + ip = ip.strip() + if ip in local_ip: continue + if ip in ip_list: continue + ip_list.append(ip) + net_ip = public.httpGet("https://api.bt.cn/api/getipaddress") + + if net_ip: + net_ip = net_ip.strip() + if not net_ip in ip_list: + ip_list.append(net_ip) + ip_list = [ip_list[-1],ip_list[0]] + return ip_list + #自签证书 def CreateSSL(): - import OpenSSL - key = OpenSSL.crypto.PKey() - key.generate_key( OpenSSL.crypto.TYPE_RSA, 2048 ) - cert = OpenSSL.crypto.X509() - cert.set_serial_number(0) - cert.get_subject().CN = public.GetLocalIp() - cert.set_issuer(cert.get_subject()) - cert.gmtime_adj_notBefore( 0 ) - cert.gmtime_adj_notAfter( 10*365*24*60*60 ) - cert.set_pubkey( key ) - cert.sign( key, 'md5' ) - cert_ca = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) - private_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) - if isinstance(cert_ca,bytes): - cert_ca = bytes.decode(cert_ca) - private_key = bytes.decode(private_key) - if len(cert_ca) > 100 and len(private_key) > 100: - public.writeFile('ssl/certificate.pem',cert_ca) - public.writeFile('ssl/privateKey.pem',private_key) - public.writeFile('/www/server/panel/data/ssl.pl','') - print('success') - return - print('error') + import base64 + userInfo = public.get_user_info() + if not userInfo: + userInfo['uid'] = 0 + userInfo['access_key'] = 'B' * 32 + domains = get_host_all() + pdata = { + "action":"get_domain_cert", + "company":"aapanel.com", + "domain":','.join(domains), + "uid":userInfo['uid'], + "access_key":userInfo['access_key'], + "panel":1 + } + cert_api = 'https://api.aapanel.com/aapanel_cert' + result = json.loads(public.httpPost(cert_api,{'data': json.dumps(pdata)})) + if 'status' in result: + if result['status']: + public.writeFile('ssl/certificate.pem',result['cert']) + public.writeFile('ssl/privateKey.pem',result['key']) + public.writeFile('ssl/baota_root.pfx',base64.b64decode(result['pfx']),'wb+') + public.writeFile('ssl/root_password.pl',result['password']) + public.writeFile('data/ssl.pl','True') + public.ExecShell("/etc/init.d/bt reload") + print('1') + return True + print('0') + return False #创建文件 def CreateFiles(path,num): @@ -386,7 +419,7 @@ def setup_idc(): filename = panelPath + '/data/o.pl' if not os.path.exists(filename): return False o = public.readFile(filename).strip() - c_url = 'http://www.bt.cn/api/idc/get_idc_info_bycode?o=%s' % o + c_url = 'https://www.bt.cn/api/idc/get_idc_info_bycode?o=%s' % o idcInfo = json.loads(public.httpGet(c_url)) if not idcInfo['status']: return False pFile = panelPath + '/config/config.json' @@ -417,7 +450,7 @@ def update_to6(): for pname in os.listdir('plugin/'): if not os.path.isdir('plugin/' + pname): continue if pname in exlodes: continue - print("|-upgrading【%s】..." % pname), + print("|-upgrading [ %s ]..." % pname), download_url = download_address + '/install/plugin/' + pname + '/install.sh' to_file = '/tmp/%s.sh' % pname public.downloadFile(download_url,to_file) @@ -442,7 +475,7 @@ def bt_cli(u_input = 0): 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("(24) Turn off Google Authenticator (17) Set log cutting on/off compression") - print("(25) Set whether to back up the panel automatically (18) Set whether to save a historical copy of the file") + print("(25) Set whether to save a historical copy of the file (18) Set whether to back up the panel automatically") print("(26) Keep/Remove local backup when backing up to cloud storage") print("(0) Cancel") print(raw_tip) @@ -609,7 +642,7 @@ def bt_cli(u_input = 0): ClearSystem() elif u_input == 16: os.system("/www/server/panel/pyenv/bin/pip install cachelib") - os.system("curl http://download.bt.cn/install/update6_en.sh|bash") + os.system("curl https://download.bt.cn/install/update6_en.sh|bash") elif u_input == 17: l_path = '/www/server/panel/data/log_not_gzip.pl' if os.path.exists(l_path): diff --git a/vhost/template/apache/node_http.conf b/vhost/template/apache/node_http.conf index b29235ea..c26d3577 100644 --- a/vhost/template/apache/node_http.conf +++ b/vhost/template/apache/node_http.conf @@ -17,7 +17,6 @@ #SSL ProxyPass /.well-known/ ! - Alias /.well-known/ /www/wwwroot/java_node_ssl/ # HTTP reverse proxy related settings begin >>>