diff --git a/.github/workflows/python-app-mysql55.yml b/.github/workflows/python-app-mysql55.yml
index ec8a8e6a1..a1866f13a 100644
--- a/.github/workflows/python-app-mysql55.yml
+++ b/.github/workflows/python-app-mysql55.yml
@@ -34,5 +34,5 @@ jobs:
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.5
- name: Start DEBUG
run: |
- gunicorn -c setting.py app:app
- python3 task.py &
+ cd web && gunicorn -c setting.py app:app
+ python3 panel_task.py &
diff --git a/.github/workflows/python-app-mysql56.yml b/.github/workflows/python-app-mysql56.yml
index 75b168717..d4b4a2618 100644
--- a/.github/workflows/python-app-mysql56.yml
+++ b/.github/workflows/python-app-mysql56.yml
@@ -34,5 +34,5 @@ jobs:
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.6
- name: Start DEBUG
run: |
- gunicorn -c setting.py app:app
- python3 task.py &
+ cd web && gunicorn -c setting.py app:app
+ python3 panel_task.py &
diff --git a/.github/workflows/python-app-mysql57.yml b/.github/workflows/python-app-mysql57.yml
index 18f1a4f90..fa5871402 100644
--- a/.github/workflows/python-app-mysql57.yml
+++ b/.github/workflows/python-app-mysql57.yml
@@ -34,5 +34,5 @@ jobs:
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.7
- name: Start DEBUG
run: |
- gunicorn -c setting.py app:app
- python3 task.py &
+ cd web && gunicorn -c setting.py app:app
+ python3 panel_task.py &
diff --git a/.github/workflows/python-app-mysql80.yml b/.github/workflows/python-app-mysql80.yml
index 2f2799bdd..c51d2ff7a 100644
--- a/.github/workflows/python-app-mysql80.yml
+++ b/.github/workflows/python-app-mysql80.yml
@@ -34,5 +34,5 @@ jobs:
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 8.0
- name: Start DEBUG
run: |
- gunicorn -c setting.py app:app
- python3 task.py &
+ cd web && gunicorn -c setting.py app:app
+ python3 panel_task.py &
diff --git a/.github/workflows/python-app-op.yml b/.github/workflows/python-app-op.yml
index 53ab5f7fc..bd26948b7 100644
--- a/.github/workflows/python-app-op.yml
+++ b/.github/workflows/python-app-op.yml
@@ -31,8 +31,8 @@ jobs:
sudo bash scripts/install_dev.sh
- name: Install OpenResty
run: |
- cd /www/server/mdserver-web/plugins/openresty && sudo bash install.sh install 1.21.4.1
+ cd /www/server/mdserver-web/plugins/openresty && sudo bash install.sh install 1.27.1
- name: Start DEBUG
run: |
- gunicorn -c setting.py app:app
- python3 task.py &
+ source /www/server/mdserver-web/bin/activate && cd web && gunicorn -c setting.py app:app
+ cd /www/server/mdserver-web && python3 panel_task.py &
diff --git a/.github/workflows/python-app-php.yml b/.github/workflows/python-app-php.yml
index 790544208..71796d15b 100644
--- a/.github/workflows/python-app-php.yml
+++ b/.github/workflows/python-app-php.yml
@@ -64,5 +64,5 @@ jobs:
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 81
- name: Start DEBUG
run: |
- gunicorn -c setting.py app:app
- python3 task.py &
+ source /www/server/mdserver-web/bin/activate && cd web && gunicorn -c setting.py app:app
+ cd /www/server/mdserver-web && python3 panel_task.py &
diff --git a/.gitignore b/.gitignore
index 63aba0e20..0061db5f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -119,7 +119,13 @@ debug.out
mdioks.session-journal
*.session
-logs/*
+temp
+
+logs/panel.pid
+logs/panel.log
+logs/panel_task.log
+logs/panel_exec.log
+logs/panel_task.lock
ssl/local
ssl/nginx
@@ -161,6 +167,7 @@ data/api.json
data/bind_domain.pl
data/unauthorized_status.pl
data/auth_secret.pl
+data/panelSpeed.pl
plugins/vip_*
plugins/own_*
@@ -170,7 +177,6 @@ plugins/l2tp
plugins/openlitespeed
plugins/tamper_proof
plugins/cryptocurrency_trade
-plugins/mtproxy
plugins/zimg
plugins/bk_demo
plugins/mail
@@ -185,4 +191,5 @@ plugins/goedge-node
plugins/goedge-happy
plugins/dztasks
-data/default_new.pl
+/logs
+/data
diff --git a/README.md b/README.md
index 1107bbb41..b169f8225 100644
--- a/README.md
+++ b/README.md
@@ -39,6 +39,10 @@ Talk is cheap, show me the code. -- linus
- [兼容性测试报告](/compatibility.md)
- [常用命令说明](/cmd.md) [ mw default ] [ mw update_dev ]
+### 特别赞助
+
+- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan1.cc)
+
### 主要插件介绍
* OpenResty - 轻量级,占有内存少,并发能力强。
@@ -88,10 +92,6 @@ PHP[72-84]支持phpMyAdmin[5.2.1]
- https://www.youtube.com/watch?v=2taa5K-Jmmw
-# 特别赞助
-
-- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan1.cc)
-
# AD - VPS推荐 - 🙏
- [ZZZ评测](https://www.zzzvps.com/)
@@ -110,11 +110,14 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:
```
-### 版本更新 0.17.3
+### 版本更新 0.18.0
-- 新增pgadmin插件。
-- 新增acme_pandominassl_apply插件。
-- 新增快捷指令:mw pgdb。
+- 重新架构面板。
+- PHP84正式版。
+- 加入alist插件。
+- 数据管理-常用命令。
+- 插件读取优化(加入缓存+定时检查状态)。
+- 恢复面板书签。
- 常规更新。
### JSDelivr安装地址
@@ -182,11 +185,12 @@ curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev
curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/install_dev.sh | bash
curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh | bash
+```
-
-
-curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_new.sh | bash
-curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_new.sh | bash
+### 旧版安装
+```
+curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/install.sh | bash
+curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/update.sh | bash
```
### 捐赠地址 USDT(TRC20)
diff --git a/app.py b/app.py
deleted file mode 100644
index 685346c77..000000000
--- a/app.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# coding:utf-8
-
-# ---------------------------------------------------------------------------------
-# MW-Linux面板
-# ---------------------------------------------------------------------------------
-# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
-# ---------------------------------------------------------------------------------
-# Author: midoks 响应状态:' + str(emsg['status']) + ' 错误类型:' + emsg[
- # 'type'] + ' 错误代码:' + emsg['detail'] + ' 响应状态:' + str(emsg['status']) + ' 错误类型:' + emsg[
- 'type'] + ' 错误代码:' + emsg['detail'] + ' 安装命令: curl https://get.acme.sh | sh 1、检查域名是否绑定到对应站点 2、检查域名是否正确解析到本服务器,或解析还未完全生效 3、如果您的站点设置了反向代理,或使用了CDN,请先将其关闭 4、如果您的站点设置了301重定向,请先将其关闭 5、如果以上检查都确认没有问题,请尝试更换DNS服务商 \
+ \
+ \
- \
- '+lan.crontab.backup_all_err+' 分区[' + rdata[i].path + ']当前Inode使用率超过' + ipre + '%,当使用率满100%时将无法在此分区创建文件,请及时清理![清理垃圾] ' + lan.get('diskinfo_span_1', [rdata[i].path]) + '[清理垃圾] 当前可用物理内存小于64M,这可能导致MySQL自动停止,站点502等错误,请尝试释放内存! ' + lan.index.user_warning + ' [不可忽略] [立即修改] '+rdata.data.content+' 注意,若您的服务器是一个容器,请取消。 安全重启有利于保障文件安全,将执行以下操作: 1.停止Web服务 2.停止MySQL服务 3.开始重启服务器 4.等待服务器启动 正在停止Web服务 正在停止MySQL服务... 开始重启服务器... 等待服务器启动... 服务器重启成功!... 方案一:修改SSH默认端口,修改SSH验证方式为数字证书,清除近期登陆日志。 方案二:购买宝塔企业运维版,一键部署安全隔离服务,高效且方便。 推荐以下一键套件,或在软件管理按需选择。 '+pname+' " + h + " http://192.168.0.1:8888 '+speed.title+' '+speed.used+'/'+speed.total+''+mspeed+' 当前状态:'+(status ? '开启' : '关闭' )+
- ' 提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换! 提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换! 提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换! 当前状态:'+(status ? '已加载' : '未加载' )+
- ' 授权账号 访问密码 重复密码 注意:设置防跨站需要重启PHP才能生效! '+lan.site.del_err+': \
- 默认站点\
- \
- \
- PHP-CLI版本\
- \
- URL后缀 许可域名 "+lan.site.limit_net_9+": "+lan.site.limit_net_10+": "+lan.site.limit_net_12+": "+lan.site.limit_net_14+": 证书获取失败:
1、绑定访问域名
2、绑定授权IP')
-
- admin_path_file = self.__file['admin_path']
- admin_path_old = '/'
- if os.path.exists(admin_path_file):
- admin_path_old = mw.readFile(admin_path_file).strip()
-
- if admin_path_old != admin_path:
- mw.writeFile(admin_path_file, admin_path)
- mw.restartMw()
- return mw.returnJson(True, '修改成功!')
-
- def closePanelApi(self):
- filename = self.__file['close']
- if os.path.exists(filename):
- os.remove(filename)
- return mw.returnJson(True, '开启成功')
- mw.writeFile(filename, 'True')
- mw.execShell("chmod 600 " + filename)
- mw.execShell("chown root.root " + filename)
- return mw.returnJson(True, '面板已关闭!')
-
- def openDebugApi(self):
- filename = 'data/debug.pl'
- if os.path.exists(filename):
- os.remove(filename)
- return mw.returnJson(True, '开发模式关闭!')
- mw.writeFile(filename, 'True')
- return mw.returnJson(True, '开发模式开启!')
-
- def setIpv6StatusApi(self):
- ipv6_file = self.__file['ipv6']
- if os.path.exists(ipv6_file):
- os.remove(ipv6_file)
- mw.writeLog('面板设置', '关闭面板IPv6兼容!')
- else:
- mw.writeFile(ipv6_file, 'True')
- mw.writeLog('面板设置', '开启面板IPv6兼容!')
- mw.restartMw()
- return mw.returnJson(True, '设置成功!')
-
- # 获取面板证书
- def getPanelSslApi(self):
- cert = self.getPanelSslData()
- return mw.getJson(cert)
-
- def getPanelSslData(self):
- rdata = {}
- choose_file = self.__file['ssl']
- rdata['choose'] = mw.readFile(choose_file)
-
-
- keyPath = 'ssl/local/private.pem'
- certPath = 'ssl/local/cert.pem'
-
- if not os.path.exists(certPath):
- mw.createLocalSSL()
-
- cert = {}
- cert['privateKey'] = mw.readFile(keyPath)
- cert['is_https'] = ''
- cert['certPem'] = mw.readFile(certPath)
- cert['info'] = mw.getCertName(certPath)
- rdata['local'] = cert
-
- panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
- if not os.path.exists(panel_ssl):
- cert['is_https'] = ''
- else:
- ssl_data = mw.readFile(panel_ssl)
- if ssl_data.find('$server_port !~ 443') != -1:
- cert['is_https'] = 'checked'
-
- keyPath = 'ssl/nginx/private.pem'
- certPath = 'ssl/nginx/cert.pem'
-
- cert = {}
- cert['privateKey'] = mw.readFile(keyPath)
- cert['certPem'] = mw.readFile(certPath)
- cert['info'] = mw.getCertName(certPath)
-
- rdata['nginx'] = cert
-
- return rdata
-
- # 面板本地SSL设置
- def setPanelLocalSslApi(self):
- cert = {}
- keyPath = 'ssl/local/private.pem'
- certPath = 'ssl/local/cert.pem'
-
- if not os.path.exists(certPath):
- mw.createLocalSSL()
-
- choose_file = self.__file['ssl']
- mw.writeFile(choose_file, 'local')
-
- mw.restartMw()
- return mw.returnJson(True, '设置成功')
-
- # 关闭SSL
- def closePanelSslApi(self):
- choose_file = self.__file['ssl']
- if os.path.exists(choose_file):
- os.remove(choose_file)
-
- local_ssl = 'ssl/local'
- if os.path.exists(local_ssl):
- mw.execShell('rm -rf '+ local_ssl)
-
- nginx_ssl = 'ssl/nginx'
- if os.path.exists(nginx_ssl):
- mw.execShell('rm -rf '+ nginx_ssl)
-
- mw.restartMw()
- return mw.returnJson(True, '关闭SSL成功')
-
- # 保存面板证书
- def savePanelSslApi(self):
-
- choose = request.form.get('choose', '').strip()
- certPem = request.form.get('certPem', '').strip()
- privateKey = request.form.get('privateKey', '').strip()
-
- if not mw.inArray(['local','nginx'], choose):
- return mw.returnJson(True, '保存错误面板SSL类型!')
-
-
- keyPath = 'ssl/'+choose+'/private.pem'
- certPath = 'ssl/'+choose+'/cert.pem'
- checkCert = '/tmp/cert.pl'
-
- if not os.path.exists(keyPath):
- return mw.returnJson(False, '【'+choose+'】SSL类型不存在,先申请!')
-
- if(privateKey.find('KEY') == -1):
- return mw.returnJson(False, '秘钥错误,请检查!')
- if(certPem.find('CERTIFICATE') == -1):
- return mw.returnJson(False, '证书错误,请检查!')
-
- mw.writeFile(checkCert, certPem)
- if privateKey:
- mw.writeFile(keyPath, privateKey)
- if certPem:
- mw.writeFile(certPath, certPem)
- if not mw.checkCert(checkCert):
- return mw.returnJson(False, '证书错误,请检查!')
- return mw.returnJson(True, '证书已保存!')
-
- # 设置面板SSL证书设置
- # def setPanelHttpToHttpsApi(self):
-
- # bind_domain = self.__file['bind_domain']
- # if not os.path.exists(bind_domain):
- # return mw.returnJson(False, '先要绑定域名!')
-
- # choose_file = self.__file['ssl']
- # choose = mw.readFile(choose_file)
- # if choose == 'local':
- # return mw.returnJson(False, '本地SSL无法使用!')
-
- # keyPath = 'ssl/nginx/private.pem'
- # if not os.path.exists(keyPath):
- # return mw.returnJson(False, '未申请SSL证书!')
-
- # is_https = request.form.get('https', '').strip()
-
- # panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
- # if not os.path.exists(panel_ssl):
- # return mw.returnJson(False, '未开启面板SSL!')
-
- # if is_https == 'false':
- # conf = mw.readFile(panel_ssl)
- # if conf:
- # if conf.find('ssl_certificate') == -1:
- # return mw.returnJson(False, '当前未开启SSL')
- # to = "#error_page 404/404.html;\n\
- # #HTTP_TO_HTTPS_START\n\
- # if ($server_port !~ 443){\n\
- # rewrite ^(/.*)$ https://$host$1 permanent;\n\
- # }\n\
- # #HTTP_TO_HTTPS_END"
- # conf = conf.replace('#error_page 404/404.html;', to)
- # mw.writeFile(panel_ssl, conf)
- # else:
- # conf = mw.readFile(panel_ssl)
- # if conf:
- # rep = "\n\\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END"
- # conf = re.sub(rep, '', conf)
- # rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}"
- # conf = re.sub(rep, '', conf)
- # mw.writeFile(panel_ssl, conf)
-
- # mw.restartNginx()
-
- # action = '开启'
- # if is_https == 'true':
- # action = '关闭'
- # return mw.returnJson(True, action + 'HTTPS跳转成功!')
-
- # 删除面板证书
- def delPanelSslApi(self):
- ip = mw.getLocalIp()
- if mw.isAppleSystem():
- ip = '127.0.0.1'
-
- port = mw.readFile('data/port.pl').strip()
-
- choose = request.form.get('choose', '').strip()
-
- if not mw.inArray(['local','nginx'], choose):
- return mw.returnJson(True, '删除错误面板SSL类型!')
-
- to_panel_url = 'http://'+ip+":"+port+'/config'
-
- if choose == 'local':
- dst_path = mw.getRunDir() + '/ssl/local'
- ssl_file = self.__file['ssl']
- if os.path.exists(dst_path):
- mw.execShell('rm -rf ' + dst_path)
- mw.execShell('rm -rf ' + ssl_file)
- mw.restartMw();
- return mw.returnJson(True, '删除本地面板SSL成功!',to_panel_url)
- else:
- return mw.returnJson(True, '已经删除本地面板SSL!',to_panel_url)
-
- if choose == 'nginx':
-
- bind_domain = self.__file['bind_domain']
- if not os.path.exists(bind_domain):
- return mw.returnJson(False, '未绑定域名!')
-
- siteName = mw.readFile(bind_domain).strip()
-
- src_path = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
-
- dst_path = mw.getRunDir() + '/ssl/nginx'
- dst_csrpath = dst_path + '/cert.pem'
- dst_keypath = dst_path + '/private.pem'
-
- if os.path.exists(src_path) or os.path.exists(dst_path):
- if os.path.exists(src_letpath):
- mw.execShell('rm -rf ' + src_letpath)
- if os.path.exists(dst_csrpath):
- mw.execShell('rm -rf ' + dst_csrpath)
- if os.path.exists(dst_keypath):
- mw.execShell('rm -rf ' + dst_keypath)
- mw.restartNginx()
- return mw.returnJson(True, '删除面板SSL成功!')
-
- mw.restartNginx()
- mw.restartMw()
- return mw.returnJson(False, '已经删除面板SSL!')
- return mw.returnJson(False, '未知类型!')
-
- # 申请面板let证书
- # def applyPanelAcmeSslApi(self):
-
- # bind_domain = self.__file['bind_domain']
- # if not os.path.exists(bind_domain):
- # return mw.returnJson(False, '先要绑定域名!')
-
- # # 生成nginx配置
- # domain = mw.readFile(bind_domain)
- # panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf"
- # dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
- # if not os.path.exists(dst_panel_path):
- # reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$"
- # if not re.match(reg, domain):
- # return mw.returnJson(False, '主域名格式不正确')
-
- # op_dir = mw.getServerDir() + "/openresty"
- # if not os.path.exists(op_dir):
- # return mw.returnJson(False, '依赖OpenResty,先安装启动它!')
-
- # content = mw.readFile(panel_tpl)
- # content = content.replace("{$PORT}", "80")
- # content = content.replace("{$SERVER_NAME}", domain)
- # content = content.replace("{$PANAL_PORT}", mw.readFile('data/port.pl'))
- # content = content.replace("{$LOGPATH}", mw.getRunDir() + '/logs')
- # content = content.replace("{$PANAL_ADDR}", mw.getRunDir())
- # mw.writeFile(dst_panel_path, content)
- # mw.restartNginx()
-
- # siteName = mw.readFile(bind_domain).strip()
- # auth_to = mw.getRunDir() + "/tmp"
- # to_args = {
- # 'domains': [siteName],
- # 'auth_type': 'http',
- # 'auth_to': auth_to,
- # }
-
- # src_path = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
- # src_csrpath = src_path + "/fullchain.pem" # 生成证书路径
- # src_keypath = src_path + "/privkey.pem" # 密钥文件路径
-
- # dst_path = mw.getRunDir() + '/ssl/nginx'
- # dst_csrpath = dst_path + '/cert.pem'
- # dst_keypath = dst_path + '/private.pem'
-
- # is_already_apply = False
-
- # if not os.path.exists(src_path):
- # import cert_api
- # data = cert_api.cert_api().applyCertApi(to_args)
- # if not data['status']:
- # msg = data['msg']
- # if type(data['msg']) != str:
- # msg = data['msg'][0]
- # emsg = data['msg'][1]['challenges'][0]['error']
- # msg = msg + '
' + isError.replace("\n", '
') + '')
- # except Exception as ex:
- # return mw.returnJson(False, '开启失败:' + str(ex))
- # mw.restartWeb()
- # return mw.returnJson(True, '开启成功,请使用https协议访问面板!')
-
- def getApi(self):
- data = {}
- return mw.getJson(data)
- ##### ----- end ----- ###
-
- # 获取临时登录列表
- def getTempLoginApi(self):
- if 'tmp_login_expire' in session:
- return mw.returnJson(False, '没有权限')
- limit = request.form.get('limit', '10').strip()
- p = request.form.get('p', '1').strip()
- tojs = request.form.get('tojs', '').strip()
-
- tempLoginM = mw.M('temp_login')
- tempLoginM.where('state=? and expire',
- (0, int(time.time()))).setField('state', -1)
-
- start = (int(p) - 1) * (int(limit))
- vlist = tempLoginM.limit(str(start) + ',' + str(limit)).order('id desc').field(
- 'id,addtime,expire,login_time,login_addr,state').select()
-
- data = {}
- data['data'] = vlist
-
- count = tempLoginM.count()
- page_args = {}
- page_args['count'] = count
- page_args['tojs'] = 'get_temp_login'
- page_args['p'] = p
- page_args['row'] = limit
-
- data['page'] = mw.getPage(page_args)
- return mw.getJson(data)
-
- # 删除临时登录
- def removeTempLoginApi(self):
- if 'tmp_login_expire' in session:
- return mw.returnJson(False, '没有权限')
- sid = request.form.get('id', '10').strip()
- if mw.M('temp_login').where('id=?', (sid,)).delete():
- mw.writeLog('面板设置', '删除临时登录连接')
- return mw.returnJson(True, '删除成功')
- return mw.returnJson(False, '删除失败')
-
- def setTempLoginApi(self):
- if 'tmp_login_expire' in session:
- return mw.returnJson(False, '没有权限')
- s_time = int(time.time())
- mw.M('temp_login').where(
- 'state=? and expire>?', (0, s_time)).delete()
- token = mw.getRandomString(48)
- salt = mw.getRandomString(12)
-
- pdata = {
- 'token': mw.md5(token + salt),
- 'salt': salt,
- 'state': 0,
- 'login_time': 0,
- 'login_addr': '',
- 'expire': s_time + 3600,
- 'addtime': s_time
- }
-
- if not mw.M('temp_login').count():
- pdata['id'] = 101
-
- if mw.M('temp_login').insert(pdata):
- mw.writeLog('面板设置', '生成临时连接,过期时间:{}'.format(
- mw.formatDate(times=pdata['expire'])))
- return mw.getJson({'status': True, 'msg': "临时连接已生成", 'token': token, 'expire': pdata['expire']})
- return mw.returnJson(False, '连接生成失败')
-
- def getTempLoginLogsApi(self):
- if 'tmp_login_expire' in session:
- return mw.returnJson(False, '没有权限')
-
- logs_id = request.form.get('id', '').strip()
- logs_id = int(logs_id)
- data = mw.M('logs').where(
- 'uid=?', (logs_id,)).order('id desc').field(
- 'id,type,uid,log,addtime').select()
- return mw.returnJson(False, 'ok', data)
-
- def checkPanelToken(self):
- api_file = self.__api_addr
- if not os.path.exists(api_file):
- return False, ''
-
- tmp = mw.readFile(api_file)
- data = json.loads(tmp)
- if data['open']:
- return True, data
- else:
- return False, ''
-
- def setStatusCodeApi(self):
- status_code = request.form.get('status_code', '').strip()
- if re.match("^\\d+$", status_code):
- status_code = int(status_code)
- if status_code != 0:
- if status_code < 100 or status_code > 999:
- return mw.returnJson(False, '状态码范围错误!')
- else:
- return mw.returnJson(False, '状态码范围错误!')
- unauthorized_status = self.__file['unauth_status']
- mw.writeFile(unauthorized_status, str(status_code))
- mw.writeLog('面板设置', '将未授权响应状态码设置为:{}'.format(status_code))
- return mw.returnJson(True, '设置成功!')
-
- def getNotifyApi(self):
- # 获取
- data = mw.getNotifyData(True)
- return mw.returnData(True, 'ok', data)
-
- def setNotifyApi(self):
- tag = request.form.get('tag', '').strip()
- data = request.form.get('data', '').strip()
-
- cfg = mw.getNotifyData(False)
-
- crypt_data = mw.enDoubleCrypt(tag, data)
- if tag in cfg:
- cfg[tag]['cfg'] = crypt_data
- else:
- t = {'cfg': crypt_data}
- cfg[tag] = t
-
- mw.writeNotify(cfg)
- return mw.returnData(True, '设置成功')
-
- def setNotifyTestApi(self):
- # 异步通知验证
- tag = request.form.get('tag', '').strip()
- tag_data = request.form.get('data', '').strip()
-
- if tag == 'tgbot':
- t = json.loads(tag_data)
- test_bool = mw.tgbotNotifyTest(t['app_token'], t['chat_id'])
- if test_bool:
- return mw.returnData(True, '验证成功')
- return mw.returnData(False, '验证失败')
-
- if tag == 'email':
- t = json.loads(tag_data)
- test_bool = mw.emailNotifyTest(t)
- if test_bool:
- return mw.returnData(True, '验证成功')
- return mw.returnData(False, '验证失败')
-
- return mw.returnData(False, '暂时未支持该验证')
-
- def setNotifyEnableApi(self):
- # 异步通知验证
- tag = request.form.get('tag', '').strip()
- tag_enable = request.form.get('enable', '').strip()
-
- data = mw.getNotifyData(False)
- op_enable = True
- op_action = '开启'
- if tag_enable != 'true':
- op_enable = False
- op_action = '关闭'
-
- if tag in data:
- data[tag]['enable'] = op_enable
-
- mw.writeNotify(data)
-
- return mw.returnData(True, op_action + '成功')
-
- def getPanelTokenApi(self):
- api_file = self.__api_addr
- tmp = mw.readFile(api_file)
- if not os.path.exists(api_file):
- ready_data = {"open": False, "token": "", "limit_addr": []}
- mw.writeFile(api_file, json.dumps(ready_data))
- mw.execShell("chmod 600 " + api_file)
- tmp = mw.readFile(api_file)
- data = json.loads(tmp)
-
- if not 'key' in data:
- data['key'] = mw.getRandomString(16)
- mw.writeFile(api_file, json.dumps(data))
-
- if 'token_crypt' in data:
- data['token'] = mw.deCrypt(data['token'], data['token_crypt'])
- else:
- token = mw.getRandomString(32)
- data['token'] = mw.md5(token)
- data['token_crypt'] = mw.enCrypt(
- data['token'], token)
- mw.writeFile(api_file, json.dumps(data))
- data['token'] = "***********************************"
-
- data['limit_addr'] = '\n'.join(data['limit_addr'])
-
- del(data['key'])
- return mw.returnJson(True, 'ok', data)
-
- def setPanelTokenApi(self):
- op_type = request.form.get('op_type', '').strip()
-
- if op_type == '1':
- token = mw.getRandomString(32)
- data['token'] = mw.md5(token)
- data['token_crypt'] = mw.enCrypt(
- data['token'], token).decode('utf-8')
- mw.writeLog('API配置', '重新生成API-Token')
- mw.writeFile(api_file, json.dumps(data))
- return mw.returnJson(True, 'ok', token)
-
- api_file = self.__api_addr
- if not os.path.exists(api_file):
- return mw.returnJson(False, "先在API接口配置")
- else:
- tmp = mw.readFile(api_file)
- data = json.loads(tmp)
-
- if op_type == '2':
- data['open'] = not data['open']
- stats = {True: '开启', False: '关闭'}
- if not 'token_crypt' in data:
- token = mw.getRandomString(32)
- data['token'] = mw.md5(token)
- data['token_crypt'] = mw.enCrypt(data['token'], token).decode('utf-8')
-
- token = stats[data['open']] + '成功!'
- mw.writeLog('API配置', '%sAPI接口' % stats[data['open']])
- mw.writeFile(api_file, json.dumps(data))
- return mw.returnJson(not not data['open'], token)
-
- elif op_type == '3':
-
- limit_addr = request.form.get('limit_addr', '').strip()
- data['limit_addr'] = limit_addr.split('\n')
- mw.writeLog('API配置', '变更IP限制为[%s]' % limit_addr)
- mw.writeFile(api_file, json.dumps(data))
- return mw.returnJson(True, '保存成功!')
-
- def renderUnauthorizedStatus(self, data):
- cfg_unauth_status = self.__file['unauth_status']
- if os.path.exists(cfg_unauth_status):
- status_code = mw.readFile(cfg_unauth_status)
- data['status_code'] = status_code
- data['status_code_msg'] = status_code
- if status_code == '0':
- data['status_code_msg'] = "默认-安全入口错误提示"
- elif status_code == '400':
- data['status_code_msg'] = "400-客户端请求错误"
- elif status_code == '401':
- data['status_code_msg'] = "401-未授权访问"
- elif status_code == '403':
- data['status_code_msg'] = "403-拒绝访问"
- elif status_code == '404':
- data['status_code_msg'] = "404-页面不存在"
- elif status_code == '408':
- data['status_code_msg'] = "408-客户端超时"
- elif status_code == '416':
- data['status_code_msg'] = "416-无效的请求"
- else:
- data['status_code'] = '0'
- data['status_code_msg'] = "默认-安全入口错误提示"
- return data
-
-
- def getTimezoneListApi(self):
- # 获取时区列表
- # pytz.all_timezones | 所有
- # pytz.common_timezones
- return pytz.all_timezones
-
- def setTimezoneApi(self):
- # 设置时区列表
- timezone = request.form.get('timezone', '').strip()
- cmd = 'timedatectl set-timezone "'+timezone+'"'
- mw.execShell(cmd)
- return mw.returnJson(True, '设置成功!')
-
-
- def getAuthSecretApi(self):
- reset = request.form.get('reset', '')
-
- import pyotp
- auth = self.__file['auth_secret']
- tag = 'mdserver-web'
- if os.path.exists(auth) and reset != '1':
- content = mw.readFile(auth)
- sec = mw.deDoubleCrypt(tag,content)
- else:
- sec = pyotp.random_base32()
- crypt_data = mw.enDoubleCrypt(tag, sec)
- mw.writeFile(auth, crypt_data)
-
- ip = mw.getHostAddr()
- url = pyotp.totp.TOTP(sec).provisioning_uri(name=ip, issuer_name=tag)
-
- rdata = {}
- rdata['secret'] = sec
- rdata['url'] = url
- return mw.returnJson(True, '设置成功!', rdata)
-
- def setAuthSecretApi(self):
- auth = self.__file['auth_secret']
- if os.path.exists(auth):
- os.remove(auth)
- return mw.returnJson(True, '关闭成功!', 0)
- else:
- return mw.returnJson(True, '开启成功!', 1)
-
-
- def get(self):
-
- data = {}
- data['title'] = mw.getConfig('title')
- data['site_path'] = mw.getWwwDir()
- data['backup_path'] = mw.getBackupDir()
- sformat = 'date +"%Y-%m-%d %H:%M:%S %Z %z"'
- data['systemdate'] = mw.execShell(sformat)[0].strip()
-
- data['port'] = mw.getHostPort()
- data['ip'] = mw.getHostAddr()
-
- admin_path_file = self.__file['admin_path']
- if not os.path.exists(admin_path_file):
- data['admin_path'] = '/'
- else:
- data['admin_path'] = mw.readFile(admin_path_file)
-
- ipv6_file = self.__file['ipv6']
- if os.path.exists(ipv6_file):
- data['ipv6'] = 'checked'
- else:
- data['ipv6'] = ''
-
- debug_file = self.__file['debug']
- if os.path.exists(debug_file):
- data['debug'] = 'checked'
- else:
- data['debug'] = ''
-
- ssl_file = self.__file['ssl']
- if os.path.exists(ssl_file):
- data['ssl'] = 'checked'
- else:
- data['ssl'] = ''
-
-
- auth_secret = self.__file['auth_secret']
- if os.path.exists(auth_secret):
- data['auth_secret'] = 'checked'
- else:
- data['auth_secret'] = ''
-
- basic_auth = self.__file['basic_auth']
- if os.path.exists(basic_auth):
- bac = mw.readFile(basic_auth)
- bac = json.loads(bac)
- if bac['open']:
- data['basic_auth'] = 'checked'
- else:
- data['basic_auth'] = ''
-
- cfg_domain = self.__file['bind_domain']
- if os.path.exists(cfg_domain):
- domain = mw.readFile(cfg_domain)
- data['bind_domain'] = domain.strip()
- else:
- data['bind_domain'] = ''
-
- data = self.renderUnauthorizedStatus(data)
-
- #api
- api_token = self.__api_addr
- if os.path.exists(api_token):
- bac = mw.readFile(api_token)
- bac = json.loads(bac)
- if bac['open']:
- data['api_token'] = 'checked'
- else:
- data['api_token'] = ''
-
- #auth
-
-
- data['site_count'] = mw.M('sites').count()
-
- data['username'] = mw.M('users').where(
- "id=?", (1,)).getField('username')
-
- data['hook_tag'] = request.args.get('tag', '')
-
- # databases hook
- database_hook_file = self.__file['hook_database']
- if os.path.exists(database_hook_file):
- df = mw.readFile(database_hook_file)
- df = json.loads(df)
- data['hook_database'] = df
- else:
- data['hook_database'] = []
-
- # menu hook
- menu_hook_file = self.__file['hook_menu']
- if os.path.exists(menu_hook_file):
- df = mw.readFile(menu_hook_file)
- df = json.loads(df)
- data['hook_menu'] = df
- else:
- data['hook_menu'] = []
-
- # global_static hook
- global_static_hook_file = self.__file['hook_global_static']
- if os.path.exists(global_static_hook_file):
- df = mw.readFile(global_static_hook_file)
- df = json.loads(df)
- data['hook_global_static'] = df
- else:
- data['hook_global_static'] = []
-
- # notiy config
- notify_data = mw.getNotifyData(True)
- notify_tag_list = ['tgbot', 'email']
- for tag in notify_tag_list:
- new_tag = 'notify_' + tag + '_enable'
- data[new_tag] = ''
- if tag in notify_data and 'enable' in notify_data[tag]:
- if notify_data[tag]['enable']:
- data[new_tag] = 'checked'
-
- return data
diff --git a/class/core/crontab_api.py b/class/core/crontab_api.py
deleted file mode 100755
index c9f7d682d..000000000
--- a/class/core/crontab_api.py
+++ /dev/null
@@ -1,694 +0,0 @@
-# coding: utf-8
-
-# ---------------------------------------------------------------------------------
-# MW-Linux面板
-# ---------------------------------------------------------------------------------
-# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
-# ---------------------------------------------------------------------------------
-# Author: midoks
' + isError.replace("\n", '
') + '')
- mw.restartWeb()
- return mw.returnJson(True, '设置成功!')
-
- def setRewriteTplApi(self):
- data = request.form.get('data', '')
- name = request.form.get('name', '')
- path = mw.getRunDir() + "/rewrite/nginx/" + name + ".conf"
- if os.path.exists(path):
- return mw.returnJson(False, '模版已经存在!')
-
- if data == "":
- return mw.returnJson(False, '模版内容不能为空!')
- ok = mw.writeFile(path, data)
- if not ok:
- return mw.returnJson(False, '模版保持失败!')
-
- return mw.returnJson(True, '设置模板成功!')
-
- def logsOpenApi(self):
- mid = request.form.get('id', '')
- name = mw.M('sites').where("id=?", (mid,)).getField('name')
-
- # NGINX
- filename = self.getHostConf(name)
- if os.path.exists(filename):
- conf = mw.readFile(filename)
- rep = self.logsPath + "/" + name + ".log"
- if conf.find(rep) != -1:
- conf = conf.replace(rep + " main", "off")
- else:
- conf = conf.replace('access_log off',
- 'access_log ' + rep + " main")
- mw.writeFile(filename, conf)
-
- mw.restartWeb()
- return mw.returnJson(True, '操作成功!')
-
- def getCertListApi(self):
- try:
- vpath = self.sslDir
- if not os.path.exists(vpath):
- os.system('mkdir -p ' + vpath)
- data = []
- for d in os.listdir(vpath):
-
- # keyPath = self.sslDir + siteName + '/privkey.pem'
- # certPath = self.sslDir + siteName + '/fullchain.pem'
-
- keyPath = vpath + '/' + d + '/privkey.pem'
- certPath = vpath + '/' + d + '/fullchain.pem'
- if os.path.exists(keyPath) and os.path.exists(certPath):
- self.saveCert(keyPath, certPath)
-
- mpath = vpath + '/' + d + '/info.json'
- if not os.path.exists(mpath):
- continue
-
- tmp = mw.readFile(mpath)
- if not tmp:
- continue
- tmp1 = json.loads(tmp)
- data.append(tmp1)
- return mw.returnJson(True, 'OK', data)
- except:
- return mw.returnJson(True, 'OK', [])
-
- def deleteSslApi(self):
- site_name = request.form.get('site_name', '')
- ssl_type = request.form.get('ssl_type', '')
-
- path = self.sslDir + '/' + site_name
- csr_path = path + '/fullchain.pem' # 生成证书路径
-
- file = self.getHostConf(site_name)
- content = mw.readFile(file)
- key_text = 'ssl_certificate'
- status = True
- if content.find(key_text) == -1:
- status = False
-
- if ssl_type == 'now':
- if status:
- return mw.returnJson(False, '使用中,先关闭再删除')
- if os.path.exists(path):
- mw.execShell('rm -rf ' + path)
- else:
- return mw.returnJson(False, '还未申请!')
- elif ssl_type == 'lets':
- ssl_lets_dir = self.sslLetsDir + '/' + site_name
- csr_lets_path = ssl_lets_dir + '/fullchain.pem' # 生成证书路径
- if mw.md5(mw.readFile(csr_lets_path)) == mw.md5(mw.readFile(csr_path)):
- return mw.returnJson(False, '使用中,先关闭再删除')
- mw.execShell('rm -rf ' + ssl_lets_dir)
- elif ssl_type == 'acme':
- ssl_acme_dir = mw.getAcmeDomainDir(site_name)
- csr_acme_path = ssl_acme_dir + '/fullchain.cer' # 生成证书路径
- if mw.md5(mw.readFile(csr_acme_path)) == mw.md5(mw.readFile(csr_path)):
- return mw.returnJson(False, '使用中,先关闭再删除')
- mw.execShell('rm -rf ' + ssl_acme_dir)
-
- # mw.restartWeb()
- return mw.returnJson(True, '删除成功')
-
- def getSslApi(self):
- site_name = request.form.get('site_name', '')
- ssl_type = request.form.get('ssl_type', '')
-
- path = self.sslDir + '/' + site_name
-
- file = self.getHostConf(site_name)
- content = mw.readFile(file)
-
- key_text = 'ssl_certificate'
- status = True
- stype = 0
- if content.find(key_text) == -1:
- status = False
- stype = -1
-
- to_https = self.isToHttps(site_name)
- sid = mw.M('sites').where("name=?", (site_name,)).getField('id')
- domains = mw.M('domain').where("pid=?", (sid,)).field('name').select()
-
- csr_path = path + '/fullchain.pem' # 生成证书路径
- key_path = path + '/privkey.pem' # 密钥文件路径
-
- cert_data = None
- if ssl_type == 'lets':
- csr_path = self.sslLetsDir + '/' + site_name + '/fullchain.pem' # 生成证书路径
- key_path = self.sslLetsDir + '/' + site_name + '/privkey.pem' # 密钥文件路径
- elif ssl_type == 'acme':
- acme_dir = mw.getAcmeDomainDir(site_name)
- csr_path = acme_dir + '/fullchain.cer' # 生成证书路径
- key_path = acme_dir + '/' + site_name + '.key' # 密钥文件路径
-
- key = mw.readFile(key_path)
- csr = mw.readFile(csr_path)
- cert_data = mw.getCertName(csr_path)
- data = {
- 'status': status,
- 'domain': domains,
- 'key': key,
- 'csr': csr,
- 'type': stype,
- 'httpTohttps': to_https,
- 'cert_data': cert_data,
- }
- return mw.returnJson(True, 'OK', data)
-
- def setSslApi(self):
- siteName = request.form.get('siteName', '')
-
- key = request.form.get('key', '')
- csr = request.form.get('csr', '')
-
- path = self.sslDir + '/' + siteName
- if not os.path.exists(path):
- mw.execShell('mkdir -p ' + path)
-
- csrpath = path + "/fullchain.pem" # 生成证书路径
- keypath = path + "/privkey.pem" # 密钥文件路径
-
- if(key.find('KEY') == -1):
- return mw.returnJson(False, '秘钥错误,请检查!')
- if(csr.find('CERTIFICATE') == -1):
- return mw.returnJson(False, '证书错误,请检查!')
-
- mw.writeFile('/tmp/cert.pl', csr)
- if not mw.checkCert('/tmp/cert.pl'):
- return mw.returnJson(False, '证书错误,请粘贴正确的PEM格式证书!')
-
- mw.backFile(keypath)
- mw.backFile(csrpath)
-
- mw.writeFile(keypath, key)
- mw.writeFile(csrpath, csr)
-
- # 写入配置文件
- result = self.setSslConf(siteName)
- if not result['status']:
- return mw.getJson(result)
-
- isError = mw.checkWebConfig()
- if(type(isError) == str):
- mw.restoreFile(keypath)
- mw.restoreFile(csrpath)
- return mw.returnJson(False, 'ERROR:
' + isError.replace("\n", '
') + '')
-
- mw.writeLog('网站管理', '证书已保存!')
- mw.restartWeb()
- return mw.returnJson(True, '证书已保存!')
-
- def setCertToSiteApi(self):
- certName = request.form.get('certName', '')
- siteName = request.form.get('siteName', '')
- try:
- path = self.sslDir + '/' + siteName.strip()
- if not os.path.exists(path):
- return mw.returnJson(False, '证书不存在!')
-
- result = self.setSslConf(siteName)
- if not result['status']:
- return mw.getJson(result)
-
- mw.restartWeb()
- mw.writeLog('网站管理', '证书已部署!')
- return mw.returnJson(True, '证书已部署!')
- except Exception as ex:
- return mw.returnJson(False, '设置错误:' + str(ex))
-
- def removeCertApi(self):
- certName = request.form.get('certName', '')
- try:
- path = self.sslDir + '/' + certName
- if not os.path.exists(path):
- return mw.returnJson(False, '证书已不存在!')
- os.system("rm -rf " + path)
- return mw.returnJson(True, '证书已删除!')
- except:
- return mw.returnJson(False, '删除失败!')
-
- def closeSslConfApi(self):
- siteName = request.form.get('siteName', '')
-
- file = self.getHostConf(siteName)
- conf = mw.readFile(file)
-
- if conf:
- rep = "\n\\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END"
- conf = re.sub(rep, '', conf)
- rep = "\\s+ssl_certificate\\s+.+;\\s+ssl_certificate_key\\s+.+;"
- conf = re.sub(rep, '', conf)
- rep = "\\s+ssl_protocols\\s+.+;\n"
- conf = re.sub(rep, '', conf)
- rep = "\\s+ssl_ciphers\\s+.+;\n"
- conf = re.sub(rep, '', conf)
- rep = "\\s+ssl_prefer_server_ciphers\\s+.+;\n"
- conf = re.sub(rep, '', conf)
- rep = "\\s+ssl_session_cache\\s+.+;\n"
- conf = re.sub(rep, '', conf)
- rep = r"\s+ssl_session_timeout\s+.+;\n"
- conf = re.sub(rep, '', conf)
- rep = r"\s+ssl_ecdh_curve\s+.+;\n"
- conf = re.sub(rep, '', conf)
- rep = r"\s+ssl_session_tickets\s+.+;\n"
- conf = re.sub(rep, '', conf)
- rep = r"\s+ssl_stapling\s+.+;\n"
- conf = re.sub(rep, '', conf)
- rep = r"\s+ssl_stapling_verify\s+.+;\n"
- conf = re.sub(rep, '', conf)
- rep = r"\s+add_header\s+.+;\n"
- conf = re.sub(rep, '', conf)
- rep = r"\s+add_header\s+.+;\n"
- conf = re.sub(rep, '', conf)
- rep = r"\s+ssl\s+on;"
- conf = re.sub(rep, '', conf)
- rep = r"\s+error_page\s497.+;"
- conf = re.sub(rep, '', conf)
- rep = r"\s+if.+server_port.+\n.+\n\s+\s*}"
- conf = re.sub(rep, '', conf)
- rep = r"\s+listen\s+443.*;"
- conf = re.sub(rep, '', conf)
- rep = r"\s+listen\s+\[\:\:\]\:443.*;"
- conf = re.sub(rep, '', conf)
- rep = r"\s+http2\s+on;"
- conf = re.sub(rep, '', conf)
- mw.writeFile(file, conf)
-
- msg = mw.getInfo('网站[{1}]关闭SSL成功!', (siteName,))
- mw.writeLog('网站管理', msg)
- mw.restartWeb()
- return mw.returnJson(True, 'SSL已关闭!')
-
- def deploySslApi(self):
- site_name = request.form.get('site_name', '')
- ssl_type = request.form.get('ssl_type', '')
-
- path = self.sslDir + '/' + site_name
- csr_path = path + '/fullchain.pem' # 生成证书路径
- key_path = path + '/privkey.pem' # 生成证书路径
-
- if not os.path.exists(path):
- os.makedirs(path)
-
- if ssl_type == 'lets':
- ssl_lets_dir = self.sslLetsDir + '/' + site_name
- lets_csrpath = ssl_lets_dir + '/fullchain.pem'
- lets_keypath = ssl_lets_dir + '/privkey.pem'
- if mw.md5(mw.readFile(lets_csrpath)) == mw.md5(mw.readFile(csr_path)):
- return mw.returnJson(False, '已部署Lets')
- else:
-
- mw.buildSoftLink(lets_csrpath, csr_path, True)
- mw.buildSoftLink(lets_keypath, key_path, True)
- mw.execShell('echo "lets" > "' + path + '/README"')
- elif ssl_type == 'acme':
- ssl_acme_dir = mw.getAcmeDir() + '/' + site_name
- if not os.path.exists(ssl_acme_dir):
- ssl_acme_dir = mw.getAcmeDir() + '/' + site_name + '_ecc'
-
- acme_csrpath = ssl_acme_dir + '/fullchain.cer'
- acme_keypath = ssl_acme_dir + '/' + site_name + '.key'
- if mw.md5(mw.readFile(acme_csrpath)) == mw.md5(mw.readFile(csr_path)):
- return mw.returnJson(False, '已部署ACME')
- else:
- mw.buildSoftLink(acme_csrpath, csr_path, True)
- mw.buildSoftLink(acme_keypath, key_path, True)
- mw.execShell('echo "acme" > "' + path + '/README"')
-
- result = self.setSslConf(site_name)
- if not result['status']:
- return mw.getJson(result)
- return mw.returnJson(True, '部署成功')
-
- def getLetsIndex(self, site_name):
- cfg = mw.getRunDir() + '/data/letsencrypt.json'
- if not os.path.exists(cfg):
- return False
-
- data = mw.readFile(cfg)
- lets_data = json.loads(data)
- order_list = lets_data['orders']
-
- for x in order_list:
- if order_list[x]['status'] == 'valid':
- for d in order_list[x]['domains']:
- if d == site_name:
- return x
- return False
-
- def renewSslApi(self):
- site_name = request.form.get('site_name', '')
- ssl_type = request.form.get('ssl_type', '')
- if ssl_type == 'lets':
- index = self.getLetsIndex(site_name)
- if index:
- import cert_api
- data = cert_api.cert_api().renewCert(index)
- return data
- else:
- return mw.returnJson(False, '无效操作')
-
- return mw.returnJson(True, '续期成功')
-
- def getLetLogsApi(self):
- log_file = mw.getRunDir() + '/logs/letsencrypt.log'
- if not os.path.exists(log_file):
- mw.execShell('touch ' + log_file)
- return mw.returnJson(True, 'OK', log_file)
-
- def createLetApi(self):
- siteName = request.form.get('siteName', '')
- domains = request.form.get('domains', '')
- force = request.form.get('force', '')
- renew = request.form.get('renew', '')
- email_args = request.form.get('email', '')
-
- domains = json.loads(domains)
- email = mw.M('users').getField('email')
- if email_args.strip() != '':
- mw.M('users').setField('email', email_args)
- email = email_args
-
- if not len(domains):
- return mw.returnJson(False, '请选择域名')
-
- host_conf_file = self.getHostConf(siteName)
- if os.path.exists(host_conf_file):
- siteConf = mw.readFile(host_conf_file)
- if siteConf.find('301-END') != -1:
- return mw.returnJson(False, '检测到您的站点做了301重定向设置,请先关闭重定向!')
-
- # 检测存在反向代理
- data_path = self.getProxyDataPath(siteName)
- data_content = mw.readFile(data_path)
- if data_content != False:
- try:
- data = json.loads(data_content)
- except:
- pass
- for proxy in data:
- proxy_dir = "{}/{}".format(self.proxyPath, siteName)
- proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf'
- if os.path.exists(proxy_dir_file):
- return mw.returnJson(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!')
-
- # fix binddir domain ssl apply question
- mw.backFile(host_conf_file)
- auth_to = self.getSitePath(siteName)
- rep = r"\s*root\s*(.+);"
- replace_root = "\n\troot " + auth_to + ";"
- siteConf = re.sub(rep, replace_root, siteConf)
- mw.writeFile(host_conf_file, siteConf)
- mw.restartWeb()
-
- to_args = {
- 'domains': domains,
- 'auth_type': 'http',
- 'auth_to': auth_to,
- }
-
- src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
- src_csrpath = src_letpath + "/fullchain.pem" # 生成证书路径
- src_keypath = src_letpath + "/privkey.pem" # 密钥文件路径
-
- dst_letpath = self.sslDir + '/' + siteName
- dst_csrpath = dst_letpath + '/fullchain.pem'
- dst_keypath = dst_letpath + '/privkey.pem'
-
- if not os.path.exists(src_letpath):
- import cert_api
- data = cert_api.cert_api().applyCertApi(to_args)
- mw.restoreFile(host_conf_file)
- if not data['status']:
- msg = data['msg']
- if type(data['msg']) != str:
- msg = data['msg'][0]
- emsg = data['msg'][1]['challenges'][0]['error']
- msg = msg + '
' + isError.replace("\n", '
') + '')
- mw.restartWeb()
- mw.removeBackFile(path)
- return save_ret_data
-
- def getRewriteConfApi(self):
- siteName = request.form.get('siteName', '')
- rewrite = self.getRewriteConf(siteName)
- return mw.getJson({'rewrite': rewrite})
-
- def getRewriteTplApi(self):
- tplname = request.form.get('tplname', '')
- file = mw.getRunDir() + '/rewrite/nginx/' + tplname + '.conf'
- if not os.path.exists(file):
- return mw.returnJson(False, '模版不存在!')
- return mw.returnJson(True, 'OK', file)
-
- def getRewriteListApi(self):
- rlist = self.getRewriteList()
- return mw.getJson(rlist)
-
- def getRootDirApi(self):
- data = {}
- data['dir'] = mw.getWwwDir()
- return mw.getJson(data)
-
- def setEndDateApi(self):
- sid = request.form.get('id', '')
- edate = request.form.get('edate', '')
- return self.setEndDate(sid, edate)
-
- def addApi(self):
- webname = request.form.get('webinfo', '')
- ps = request.form.get('ps', '')
- path = request.form.get('path', '')
- version = request.form.get('version', '')
- port = request.form.get('port', '')
- return self.add(webname, port, ps, path, version)
-
- def checkWebStatusApi(self):
- '''
- 创建站点检查web服务
- '''
- if not mw.isInstalledWeb():
- return mw.returnJson(False, '请安装并启动OpenResty服务!')
-
- # 这个快点
- pid = mw.getServerDir() + '/openresty/nginx/logs/nginx.pid'
- if not os.path.exists(pid):
- return mw.returnJson(False, '请启动OpenResty服务!')
-
- # path = mw.getServerDir() + '/openresty/init.d/openresty'
- # data = mw.execShell(path + " status")
- # if data[0].strip().find('stopped') != -1:
- # return mw.returnJson(False, '请启动OpenResty服务!')
-
- # import plugins_api
- # data = plugins_api.plugins_api().run('openresty', 'status')
- # if data[0].strip() == 'stop':
- # return mw.returnJson(False, '请启动OpenResty服务!')
-
- return mw.returnJson(True, 'OK')
-
- def addDomainApi(self):
- isError = mw.checkWebConfig()
- if isError != True:
- return mw.returnJson(False, 'ERROR: 检测到配置文件有错误,请先排除后再操作
' + isError.replace("\n", '
') + '')
-
- domain = request.form.get('domain', '')
- webname = request.form.get('webname', '')
- pid = request.form.get('id', '')
- return self.addDomain(domain, webname, pid)
-
- def addDomain(self, domain, webname, pid):
- if len(domain) < 3:
- return mw.returnJson(False, '域名不能为空!')
- domains = domain.split(',')
- for domain in domains:
- if domain == "":
- continue
- domain = domain.split(':')
- # print domain
- domain_name = self.toPunycode(domain[0])
- domain_port = '80'
-
- reg = r"^([\w\-\*]{1,100}\.){1,4}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$"
- if not re.match(reg, domain_name):
- return mw.returnJson(False, '域名格式不正确!')
-
- if len(domain) == 2:
- domain_port = domain[1]
- if domain_port == "":
- domain_port = "80"
-
- if not mw.checkPort(domain_port):
- return mw.returnJson(False, '端口范围不合法!')
-
- opid = mw.M('domain').where(
- "name=? AND (port=? OR pid=?)", (domain_name, domain_port, pid,)).getField('pid')
- # print(opid)
- is_bind = False
- if type(opid) == list and len(opid) > 0:
- is_bind = True
-
- if type(opid) == int and opid > 0:
- is_bind = True
-
- if type(opid) == str and int(opid) > 0:
- is_bind = True
-
- if is_bind:
- return mw.returnJson(False, '您添加的域名[{}],已绑定!'.format(domain_name))
- # if mw.M('sites').where('id=?', (opid,)).count():
- # return mw.returnJson(False, '指定域名已绑定过!')
- # mw.M('domain').where('pid=?', (opid,)).delete()
-
- if mw.M('binding').where('domain=?', (domain,)).count():
- return mw.returnJson(False, '您添加的域名,子目录已绑定!')
-
- self.nginxAddDomain(webname, domain_name, domain_port)
-
- mw.restartWeb()
- msg = mw.getInfo('网站[{1}]添加域名[{2}]成功!', (webname, domain_name))
- mw.writeLog('网站管理', msg)
- mw.M('domain').add('pid,name,port,addtime',
- (pid, domain_name, domain_port, mw.getDate()))
-
- self.runHook('site_cb', 'add')
- return mw.returnJson(True, '域名添加成功!')
-
- def addDirBindApi(self):
- pid = request.form.get('id', '')
- domain = request.form.get('domain', '')
- dirName = request.form.get('dirName', '')
- tmp = domain.split(':')
- domain = tmp[0]
- port = '80'
- if len(tmp) > 1:
- port = tmp[1]
- if dirName == '':
- mw.returnJson(False, '目录不能为空!')
-
- reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$"
- if not re.match(reg, domain):
- return mw.returnJson(False, '主域名格式不正确!')
-
- siteInfo = mw.M('sites').where(
- "id=?", (pid,)).field('id,path,name').find()
- webdir = siteInfo['path'] + '/' + dirName
-
- if mw.M('binding').where("domain=?", (domain,)).count() > 0:
- return mw.returnJson(False, '您添加的域名已存在!')
- if mw.M('domain').where("name=?", (domain,)).count() > 0:
- return mw.returnJson(False, '您添加的域名已存在!')
-
- filename = self.getHostConf(siteInfo['name'])
- conf = mw.readFile(filename)
- if conf:
- rep = r"enable-php-([0-9]{2,3})\.conf"
- tmp = re.search(rep, conf).groups()
- version = tmp[0]
-
- source_dirbind_tpl = mw.getRunDir() + '/data/tpl/nginx_dirbind.conf'
- content = mw.readFile(source_dirbind_tpl)
- content = content.replace('{$PORT}', port)
- content = content.replace('{$PHPVER}', version)
- content = content.replace('{$DIRBIND}', domain)
- content = content.replace('{$ROOT_DIR}', webdir)
- content = content.replace('{$SERVER_MAIN}', siteInfo['name'])
- content = content.replace('{$OR_REWRITE}', self.rewritePath)
- content = content.replace('{$PHP_DIR}', self.setupPath + '/php')
- content = content.replace('{$LOGPATH}', mw.getLogsDir())
-
- conf += "\r\n" + content
- mw.backFile(filename)
- mw.writeFile(filename, conf)
- conf = mw.readFile(filename)
-
- # 检查配置是否有误
- isError = mw.checkWebConfig()
- if isError != True:
- mw.restoreFile(filename)
- return mw.returnJson(False, 'ERROR:
' + isError.replace("\n", '
') + '')
-
- mw.M('binding').add('pid,domain,port,path,addtime',
- (pid, domain, port, dirName, mw.getDate()))
-
- msg = mw.getInfo('网站[{1}]子目录[{2}]绑定到[{3}]',
- (siteInfo['name'], dirName, domain))
- mw.writeLog('网站管理', msg)
- mw.restartWeb()
- mw.removeBackFile(filename)
- return mw.returnJson(True, '添加成功!')
-
- def delDirBindApi(self):
- mid = request.form.get('id', '')
- binding = mw.M('binding').where(
- "id=?", (mid,)).field('id,pid,domain,path').find()
- siteName = mw.M('sites').where(
- "id=?", (binding['pid'],)).getField('name')
-
- filename = self.getHostConf(siteName)
- conf = mw.readFile(filename)
- if conf:
- rep = "\s*.+BINDING-" + \
- binding['domain'] + \
- "-START(.|\n)+BINDING-" + binding['domain'] + "-END"
- conf = re.sub(rep, '', conf)
- mw.writeFile(filename, conf)
-
- mw.M('binding').where("id=?", (mid,)).delete()
-
- filename = self.getDirBindRewrite(siteName, binding['path'])
- if os.path.exists(filename):
- os.remove(filename)
- mw.restartWeb()
- msg = mw.getInfo('删除网站[{1}]子目录[{2}]绑定',
- (siteName, binding['path']))
- mw.writeLog('网站管理', msg)
- return mw.returnJson(True, '删除成功!')
-
- # 取子目录Rewrite
- def getDirBindRewriteApi(self):
- mid = request.form.get('id', '')
- add = request.form.get('add', '0')
- find = mw.M('binding').where(
- "id=?", (mid,)).field('id,pid,domain,path').find()
- site = mw.M('sites').where(
- "id=?", (find['pid'],)).field('id,name,path').find()
-
- filename = self.getDirBindRewrite(site['name'], find['path'])
- if add == '1':
- mw.writeFile(filename, '')
- file = self.getHostConf(site['name'])
- conf = mw.readFile(file)
- domain = find['domain']
- rep = "\n#BINDING-" + domain + \
- "-START(.|\n)+BINDING-" + domain + "-END"
- tmp = re.search(rep, conf).group()
- dirConf = tmp.replace('rewrite/' + site['name'] + '.conf;', 'rewrite/' + site[
- 'name'] + '_' + find['path'] + '.conf;')
- conf = conf.replace(tmp, dirConf)
- mw.writeFile(file, conf)
- data = {}
- data['rewrite_dir'] = self.rewritePath
- data['status'] = False
- if os.path.exists(filename):
- data['status'] = True
- data['data'] = mw.readFile(filename)
- data['rlist'] = []
- for ds in os.listdir(self.rewritePath):
- if ds[0:1] == '.':
- continue
- if ds == 'list.txt':
- continue
- data['rlist'].append(ds[0:len(ds) - 5])
- data['filename'] = filename
- return mw.getJson(data)
-
- # 修改物理路径
- def setPathApi(self):
- mid = request.form.get('id', '')
- path = request.form.get('path', '')
-
- path = self.getPath(path)
- if path == "" or mid == '0':
- return mw.returnJson(False, "目录不能为空!")
-
- import files_api
- if not files_api.files_api().checkDir(path):
- return mw.returnJson(False, "不能以系统关键目录作为站点目录")
-
- siteFind = mw.M("sites").where(
- "id=?", (mid,)).field('path,name').find()
- if siteFind["path"] == path:
- return mw.returnJson(False, "与原路径一致,无需修改!")
- file = self.getHostConf(siteFind['name'])
- conf = mw.readFile(file)
- if conf:
- conf = conf.replace(siteFind['path'], path)
- mw.writeFile(file, conf)
-
- # 创建basedir
- # userIni = path + '/.user.ini'
- # if os.path.exists(userIni):
- # mw.execShell("chattr -i " + userIni)
- # mw.writeFile(userIni, 'open_basedir=' + path + '/:/tmp/:/proc/')
- # mw.execShell('chmod 644 ' + userIni)
- # mw.execShell('chown root:root ' + userIni)
- # mw.execShell('chattr +i ' + userIni)
-
- mw.restartWeb()
- mw.M("sites").where("id=?", (mid,)).setField('path', path)
- msg = mw.getInfo('修改网站[{1}]物理路径成功!', (siteFind['name'],))
- mw.writeLog('网站管理', msg)
- return mw.returnJson(True, "设置成功!")
-
- # 设置当前站点运行目录
- def setSiteRunPathApi(self):
- mid = request.form.get('id', '')
- runPath = request.form.get('runPath', '')
- siteName = mw.M('sites').where('id=?', (mid,)).getField('name')
- sitePath = mw.M('sites').where('id=?', (mid,)).getField('path')
-
- newPath = sitePath + runPath
-
- # 处理Nginx
- filename = self.getHostConf(siteName)
- if os.path.exists(filename):
- conf = mw.readFile(filename)
- rep = r'\s*root\s*(.+);'
- path = re.search(rep, conf).groups()[0]
- conf = conf.replace(path, newPath)
- mw.writeFile(filename, conf)
-
- self.setDirUserINI(sitePath, runPath)
-
- mw.restartWeb()
- return mw.returnJson(True, '设置成功!')
-
- # 设置目录加密
- def setHasPwdApi(self):
- username = request.form.get('username', '')
- password = request.form.get('password', '')
- siteName = request.form.get('siteName', '')
- mid = request.form.get('id', '')
-
- if len(username.strip()) == 0 or len(password.strip()) == 0:
- return mw.returnJson(False, '用户名或密码不能为空!')
-
- if siteName == '':
- siteName = mw.M('sites').where('id=?', (mid,)).getField('name')
-
- # self.closeHasPwd(get)
- filename = self.passPath + '/' + siteName + '.pass'
- # print(filename)
- passconf = username + ':' + mw.hasPwd(password)
-
- if siteName == 'phpmyadmin':
- configFile = self.getHostConf('phpmyadmin')
- else:
- configFile = self.getHostConf(siteName)
-
- # 处理Nginx配置
- conf = mw.readFile(configFile)
- if conf:
- rep = '#error_page 404 /404.html;'
- if conf.find(rep) == -1:
- rep = '#error_page 404/404.html;'
- data = '''
- #AUTH_START
- auth_basic "Authorization";
- auth_basic_user_file %s;
- #AUTH_END''' % (filename,)
- conf = conf.replace(rep, rep + data)
- mw.writeFile(configFile, conf)
- # 写密码配置
- passDir = self.passPath
- if not os.path.exists(passDir):
- mw.execShell('mkdir -p ' + passDir)
- mw.writeFile(filename, passconf)
-
- mw.restartWeb()
- msg = mw.getInfo('设置网站[{1}]为需要密码认证!', (siteName,))
- mw.writeLog("网站管理", msg)
- return mw.returnJson(True, '设置成功!')
-
- # 取消目录加密
- def closeHasPwdApi(self):
- siteName = request.form.get('siteName', '')
- mid = request.form.get('id', '')
- if siteName == '':
- siteName = mw.M('sites').where('id=?', (mid,)).getField('name')
-
- if siteName == 'phpmyadmin':
- configFile = self.getHostConf('phpmyadmin')
- else:
- configFile = self.getHostConf(siteName)
-
- if os.path.exists(configFile):
- conf = mw.readFile(configFile)
- rep = r"\n\s*#AUTH_START(.|\n){1,200}#AUTH_END"
- conf = re.sub(rep, '', conf)
- mw.writeFile(configFile, conf)
-
- mw.restartWeb()
- msg = mw.getInfo('清除网站[{1}]的密码认证!', (siteName,))
- mw.writeLog("网站管理", msg)
- return mw.returnJson(True, '设置成功!')
-
- def delDomainApi(self):
- domain = request.form.get('domain', '')
- webname = request.form.get('webname', '')
- port = request.form.get('port', '')
- pid = request.form.get('id', '')
-
- find = mw.M('domain').where("pid=? AND name=?",
- (pid, domain)).field('id,name').find()
-
- domain_count = mw.M('domain').where("pid=?", (pid,)).count()
- if domain_count == 1:
- return mw.returnJson(False, '最后一个域名不能删除!')
-
- file = self.getHostConf(webname)
- conf = mw.readFile(file)
- if conf:
- # 删除域名
- rep = r"server_name\s+(.+);"
- tmp = re.search(rep, conf).group()
- newServerName = tmp.replace(' ' + domain + ';', ';')
- newServerName = newServerName.replace(' ' + domain + ' ', ' ')
- conf = conf.replace(tmp, newServerName)
-
- # 删除端口
- rep = r"listen\s+([0-9]+);"
- tmp = re.findall(rep, conf)
- port_count = mw.M('domain').where(
- 'pid=? AND port=?', (pid, port)).count()
- if mw.inArray(tmp, port) == True and port_count < 2:
- rep = r"\n*\s+listen\s+" + port + ";"
- conf = re.sub(rep, '', conf)
- # 保存配置
- mw.writeFile(file, conf)
-
- mw.M('domain').where("id=?", (find['id'],)).delete()
- msg = mw.getInfo('网站[{1}]删除域名[{2}]成功!', (webname, domain))
- mw.writeLog('网站管理', msg)
- mw.restartWeb()
- return mw.returnJson(True, '站点删除成功!')
-
- def deleteApi(self):
- sid = request.form.get('id', '')
- webname = request.form.get('webname', '')
- path = request.form.get('path', '0')
- return self.delete(sid, webname, path)
-
- # 操作 重定向配置
- def operateRedirectConf(self, siteName, method='start'):
- vhost_file = self.vhostPath + '/' + siteName + '.conf'
- content = mw.readFile(vhost_file)
-
- cnf_301 = '''#301-START
- include %s/*.conf;
- #301-END''' % (self.getRedirectPath( siteName))
-
- cnf_301_source = '#301-START'
- # print('operateRedirectConf', content.find('#301-END'))
- if content.find('#301-END') != -1:
- if method == 'stop':
- rep = '#301-START(\n|.){1,500}#301-END'
- content = re.sub(rep, '#301-START', content)
- else:
- if method == 'start':
- content = re.sub(cnf_301_source, cnf_301, content)
-
- mw.writeFile(vhost_file, content)
-
- # get_redirect_status
- def getRedirectApi(self):
- _siteName = request.form.get("siteName", '')
-
- # read data base
- data_path = self.getRedirectDataPath(_siteName)
- data_content = mw.readFile(data_path)
- if data_content == False:
- mw.execShell("mkdir {}/{}".format(self.redirectPath, _siteName))
- return mw.returnJson(True, "", {"result": [], "count": 0})
- # get
- # conf_path = "{}/{}/*.conf".format(self.redirectPath, siteName)
- # conf_list = glob.glob(conf_path)
- # if conf_list == []:
- # return mw.returnJson(True, "", {"result": [], "count": 0})
- try:
- data = json.loads(data_content)
- except:
- mw.execShell("rm -rf {}/{}".format(self.redirectPath, _siteName))
- return mw.returnJson(True, "", {"result": [], "count": 0})
-
- # 处理301信息
- return mw.returnJson(True, "ok", {"result": data, "count": len(data)})
-
- def getRedirectConfApi(self):
- _siteName = request.form.get("siteName", '')
- _id = request.form.get("id", '')
- if _id == '' or _siteName == '':
- return mw.returnJson(False, "必填项不能为空!")
-
- data = mw.readFile(
- "{}/{}/{}.conf".format(self.redirectPath, _siteName, _id))
- if data == False:
- return mw.returnJson(False, "获取失败!")
- return mw.returnJson(True, "ok", {"result": data})
-
- def saveRedirectConfApi(self):
- _siteName = request.form.get("siteName", '')
- _id = request.form.get("id", '')
- _config = request.form.get("config", "")
- if _id == '' or _siteName == '':
- return mw.returnJson(False, "必填项不能为空!")
-
- _old_config = mw.readFile(
- "{}/{}/{}.conf".format(self.redirectPath, _siteName, _id))
- if _old_config == False:
- return mw.returnJson(False, "非法操作")
-
- mw.writeFile("{}/{}/{}.conf".format(self.redirectPath,
- _siteName, _id), _config)
- rule_test = mw.checkWebConfig()
- if rule_test != True:
- mw.writeFile("{}/{}/{}.conf".format(self.redirectPath,
- _siteName, _id), _old_config)
- return mw.returnJson(False, "OpenResty 配置测试不通过, 请重试: {}".format(rule_test))
-
- self.operateRedirectConf(_siteName, 'start')
- mw.restartWeb()
- return mw.returnJson(True, "ok")
-
- # get redirect status
- def setRedirectApi(self):
- _siteName = request.form.get("siteName", '')
- # from (example.com / /test/)
- _from = request.form.get("from", '')
- _to = request.form.get("to", '') # redirect to
- _type = request.form.get("type", '') # path / domain
- _rType = request.form.get("r_type", '') # redirect type
- _keepPath = request.form.get("keep_path", '') # keep path
-
- if _siteName == '' or _from == '' or _to == '' or _type == '' or _rType == '':
- return mw.returnJson(False, "必填项不能为空!")
-
- data_path = self.getRedirectDataPath(_siteName)
- data_content = mw.readFile(
- data_path) if os.path.exists(data_path) else ""
- data = json.loads(data_content) if data_content != "" else []
-
- _rTypeCode = 0 if _rType == "301" else 1
- _typeCode = 0 if _type == "path" else 1
- _keepPath = 1 if _keepPath == "1" else 0
-
- # check if domain exists in site
- if _typeCode == 1:
- pid = mw.M('domain').where("name=?", (_siteName,)).field(
- 'id,pid,name,port,addtime').select()
- site_domain_lists = mw.M('domain').where("pid=?", (pid[0]['pid'],)).field(
- 'name').select()
- found = False
- for item in site_domain_lists:
- if item['name'] == _from:
- found = True
- break
- if found == False:
- return mw.returnJson(False, "域名不存在!")
-
- file_content = ""
- # path
- if _typeCode == 0:
- redirect_type = "permanent" if _rTypeCode == 0 else "redirect"
- if not _from.startswith("/"):
- _from = "/{}".format(_from)
- if _keepPath == 1:
- _to = "{}$1".format(_to)
- _from = "{}(.*)".format(_from)
- file_content = "rewrite ^{} {} {};".format(
- _from, _to, redirect_type)
- # domain
- else:
- if _keepPath == 1:
- _to = "{}$request_uri".format(_to)
-
- redirect_type = "301" if _rTypeCode == 0 else "302"
- _if = "if ($host ~ '^{}')".format(_from)
- _return = "return {} {}; ".format(redirect_type, _to)
- file_content = _if + "{\r\n " + _return + "\r\n}"
-
- _id = mw.md5("{}+{}".format(file_content, _siteName))
-
- # 防止规则重复
- for item in data:
- if item["r_from"] == _from:
- return mw.returnJson(False, "重复的规则!")
-
- rep = r"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, _to):
- return mw.returnJson(False, "错误的目标地址")
-
- # write data json file
- data.append({"r_from": _from, "type": _typeCode, "r_type": _rTypeCode,
- "r_to": _to, 'keep_path': _keepPath, 'id': _id})
- mw.writeFile(data_path, json.dumps(data))
- mw.writeFile("{}/{}.conf".format(self.getRedirectPath(_siteName), _id), file_content)
-
- self.operateRedirectConf(_siteName, 'start')
- mw.restartWeb()
- return mw.returnJson(True, "ok")
-
- # 删除指定重定向
- def delRedirectApi(self):
- _siteName = request.form.get("siteName", '')
- _id = request.form.get("id", '')
- if _id == '' or _siteName == '':
- return mw.returnJson(False, "必填项不能为空!")
-
- try:
- data_path = self.getRedirectDataPath(_siteName)
- data_content = mw.readFile(
- data_path) if os.path.exists(data_path) else ""
- data = json.loads(data_content) if data_content != "" else []
- for item in data:
- if item["id"] == _id:
- data.remove(item)
- break
- # write database
- mw.writeFile(data_path, json.dumps(data))
- # data is empty ,should stop
- if len(data) == 0:
- self.operateRedirectConf(_siteName, 'stop')
- # remove conf file
- mw.execShell(
- "rm -rf {}/{}.conf".format(self.getRedirectPath(_siteName), _id))
- except:
- return mw.returnJson(False, "删除失败!")
- return mw.returnJson(True, "删除成功!")
-
- # 操作 反向代理配置
- def operateProxyConf(self, siteName, method='start'):
- vhost_file = self.vhostPath + '/' + siteName + '.conf'
- content = mw.readFile(vhost_file)
-
- proxy_cnf = '''#PROXY-START
- include %s/*.conf;
- #PROXY-END''' % (self.getProxyPath(siteName))
-
- proxy_cnf_source = '#PROXY-START'
-
- if content.find('#PROXY-END') != -1:
- if method == 'stop':
- rep = '#PROXY-START(\n|.){1,500}#PROXY-END'
- content = re.sub(rep, '#PROXY-START', content)
- else:
- if method == 'start':
- content = re.sub(proxy_cnf_source, proxy_cnf, content)
-
- mw.writeFile(vhost_file, content)
-
- def getProxyConfApi(self):
- _siteName = request.form.get("siteName", '')
- _id = request.form.get("id", '')
- if _id == '' or _siteName == '':
- return mw.returnJson(False, "必填项不能为空!")
-
- conf_file = "{}/{}/{}.conf".format(self.proxyPath, _siteName, _id)
- if not os.path.exists(conf_file):
- conf_file = "{}/{}/{}.conf.txt".format(
- self.proxyPath, _siteName, _id)
-
- data = mw.readFile(conf_file)
- if data == False:
- return mw.returnJson(False, "获取失败!")
- return mw.returnJson(True, "ok", {"result": data})
-
- def setProxyStatusApi(self):
- _siteName = request.form.get("siteName", '')
- _status = request.form.get("status", '')
- _id = request.form.get("id", '')
- if _status == '' or _siteName == '' or _id == '':
- return mw.returnJson(False, "必填项不能为空!")
-
- conf_file = "{}/{}/{}.conf".format(self.proxyPath, _siteName, _id)
- conf_txt = "{}/{}/{}.conf.txt".format(self.proxyPath, _siteName, _id)
-
- if _status == '1':
- mw.execShell('mv ' + conf_txt + ' ' + conf_file)
- else:
- mw.execShell('mv ' + conf_file + ' ' + conf_txt)
-
- mw.restartWeb()
- return mw.returnJson(True, "OK")
-
- def saveProxyConfApi(self):
- _siteName = request.form.get("siteName", '')
- _id = request.form.get("id", '')
- _config = request.form.get("config", "")
- if _id == '' or _siteName == '':
- return mw.returnJson(False, "必填项不能为空!")
-
- proxy_file = "{}/{}/{}.conf".format(self.proxyPath, _siteName, _id)
- mw.backFile(proxy_file)
- mw.writeFile(proxy_file, _config)
- rule_test = mw.checkWebConfig()
- if rule_test != True:
- mw.restoreFile(proxy_file)
- mw.removeBackFile(proxy_file)
- return mw.returnJson(False, "OpenResty 配置测试不通过, 请重试: {}".format(rule_test))
-
- mw.removeBackFile(proxy_file)
- self.operateRedirectConf(_siteName, 'start')
- mw.restartWeb()
- return mw.returnJson(True, "ok")
-
- # 读取 网站 反向代理列表
- def getProxyListApi(self):
- _siteName = request.form.get('siteName', '')
-
- data_path = self.getProxyDataPath(_siteName)
- data_content = mw.readFile(data_path)
-
- # not exists
- if data_content == False:
- mw.execShell("mkdir {}/{}".format(self.proxyPath, _siteName))
- return mw.returnJson(True, "", {"result": [], "count": 0})
-
- try:
- data = json.loads(data_content)
- except:
- mw.execShell("rm -rf {}/{}".format(self.proxyPath, _siteName))
- return mw.returnJson(True, "", {"result": [], "count": 0})
-
- tmp = []
- for proxy in data:
- proxy_dir = "{}/{}".format(self.proxyPath, _siteName)
- proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf'
- if os.path.exists(proxy_dir_file):
- proxy['status'] = True
- else:
- proxy['status'] = False
- tmp.append(proxy)
-
- return mw.returnJson(True, "ok", {"result": data, "count": len(data)})
-
- # 设置 网站 反向代理列表
- def setProxyApi(self):
- _siteName = request.form.get('siteName', '')
- _from = request.form.get('from', '')
- _to = request.form.get('to', '')
- _host = request.form.get('host', '')
- _name = request.form.get('name', '')
- _open_proxy = request.form.get('open_proxy', '')
- _open_cache = request.form.get('open_cache', '')
- _cache_time = request.form.get('cache_time', '')
- _id = request.form.get('id', '')
-
- # print(_name, _siteName, _from, _to, _host)
- if _name == "" or _siteName == "" or _from == "" or _to == "" or _host == "":
- return mw.returnJson(False, "必填项不能为空")
-
- rep = r"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, _to):
- return mw.returnJson(False, "错误的目标地址!")
-
- # get host from url
- try:
- if _host == "$host":
- host_tmp = urlparse(_to)
- _host = host_tmp.netloc
- except:
- return mw.returnJson(False, "错误的目标地址")
-
- proxy_site_path = self.getProxyDataPath(_siteName)
- data_content = mw.readFile(
- proxy_site_path) if os.path.exists(proxy_site_path) else ""
- data = json.loads(data_content) if data_content != "" else []
-
- proxy_action = 'add'
- if _id == "":
- _id = mw.md5("{}".format(_name))
- else:
- proxy_action = 'edit'
-
- if proxy_action == "add":
- for item in data:
- if item["name"] == _name:
- return mw.returnJson(False, "名称重复!!")
- if item["from"] == _from:
- return mw.returnJson(False, "代理目录已存在!!")
-
- tpl = "#PROXY-START\n\
-location ^~ {from} {\n\
- proxy_pass {to};\n\
- proxy_set_header Host {host};\n\
- proxy_ssl_server_name on;\n\
- proxy_set_header X-Real-IP $remote_addr;\n\
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\
- proxy_set_header REMOTE-HOST $remote_addr;\n\
- proxy_set_header Upgrade $http_upgrade;\n\
- proxy_set_header Connection $connection_upgrade;\n\
- proxy_http_version 1.1;\n\
- \n\
- add_header X-Cache $upstream_cache_status;\n\
- \n\
- {proxy_cache}\n\
-}\n\
-# PROXY-END"
-
- tpl_proxy_cache = r"\n\
- if ( $uri ~* \"\.(gif|png|jpg|css|js|woff|woff2)$\" )\n\
- {\n\
- expires {cache_time}m;\n\
- }\n\
- proxy_ignore_headers Set-Cookie Cache-Control expires;\n\
- proxy_cache mw_cache;\n\
- proxy_cache_key \"$host$uri$is_args$args\";\n\
- proxy_cache_valid 200 304 301 302 {cache_time}m;\n\
-"
-
- tpl_proxy_nocache = r"\n\
- set $static_files_app 0; \n\
- if ( $uri ~* \"\.(gif|png|jpg|css|js|woff|woff2)$\" )\n\
- {\n\
- set $static_files_app 1;\n\
- expires 12h;\n\
- }\n\
- if ( $static_files_app = 0 )\n\
- {\n\
- add_header Cache-Control no-cache;\n\
- }\n\
-"
-
- # replace
- if _from[0] != '/':
- _from = '/' + _from
- tpl = tpl.replace("{from}", _from, 999)
- tpl = tpl.replace("{to}", _to)
- tpl = tpl.replace("{host}", _host, 999)
- tpl = tpl.replace("{cache_time}", _cache_time, 999)
-
- if _open_cache == 'on':
- tpl_proxy_cache = tpl_proxy_cache.replace(
- "{cache_time}", _cache_time, 999)
- tpl = tpl.replace("{proxy_cache}", tpl_proxy_cache, 999)
- else:
- tpl = tpl.replace("{proxy_cache}", tpl_proxy_nocache, 999)
-
-
- conf_proxy = "{}/{}.conf".format(self.getProxyPath(_siteName), _id)
- conf_bk = "{}/{}.conf.txt".format(self.getProxyPath(_siteName), _id)
- mw.writeFile(conf_proxy, tpl)
-
- rule_test = mw.checkWebConfig()
- if rule_test != True:
- os.remove(conf_proxy)
- return mw.returnJson(False, "OpenResty配置测试不通过, 请重试: {}".format(rule_test))
-
- if proxy_action == "add":
- # 添加代理
- _id = mw.md5("{}".format(_name))
- for item in data:
- if item["name"] == _name:
- return mw.returnJson(False, "名称重复!")
- if item["from"] == _from:
- return mw.returnJson(False, "代理目录已存在!")
- data.append({
- "name": _name,
- "from": _from,
- "to": _to,
- "host": _host,
- "open_cache": _open_cache,
- "open_proxy": _open_proxy,
- "cache_time": _cache_time,
- "id": _id,
- })
- else:
- # 修改代理
- dindex = -1
- for x in range(len(data)):
- if data[x]["id"] == _id:
- dindex = x
- break
- if dindex < 0:
- return mw.returnJson(False, "异常请求")
- data[dindex]['from'] = _from
- data[dindex]['to'] = _to
- data[dindex]['host'] = _host
- data[dindex]['open_cache'] = _open_cache
- data[dindex]['open_proxy'] = _open_proxy
- data[dindex]['cache_time'] = _cache_time
-
- if _open_proxy != 'on':
- os.rename(conf_proxy, conf_bk)
-
- mw.writeFile(proxy_site_path, json.dumps(data))
- self.operateProxyConf(_siteName, 'start')
- mw.restartWeb()
- return mw.returnJson(True, "ok", {"hash": _id})
-
- def delProxyApi(self):
- _siteName = request.form.get("siteName", '')
- _id = request.form.get("id", '')
- if _id == '' or _siteName == '':
- return mw.returnJson(False, "必填项不能为空!")
-
- try:
- data_path = self.getProxyDataPath(_siteName)
- data_content = mw.readFile(
- data_path) if os.path.exists(data_path) else ""
- data = json.loads(data_content) if data_content != "" else []
- for item in data:
- if item["id"] == _id:
- data.remove(item)
- break
- # write database
- mw.writeFile(data_path, json.dumps(data))
-
- # data is empty,should stop
- if len(data) == 0:
- self.operateProxyConf(_siteName, 'stop')
- # remove conf file
- cmd = "rm -rf {}/{}.conf*".format(
- self.getProxyPath(_siteName), _id)
- mw.execShell(cmd)
- except:
- return mw.returnJson(False, "删除反代失败!")
-
- mw.restartWeb()
- return mw.returnJson(True, "删除反代成功!")
-
- def getSiteTypesApi(self):
- # 取网站分类
- data = mw.M("site_types").field("id,name").order("id asc").select()
- data.insert(0, {"id": 0, "name": "默认分类"})
- return mw.getJson(data)
-
- def getSiteDocApi(self):
- stype = request.form.get('type', '0').strip()
- vlist = []
- vlist.append('')
- vlist.append(mw.getServerDir() +
- '/openresty/nginx/html/index.html')
- vlist.append(mw.getServerDir() + '/openresty/nginx/html/404.html')
- vlist.append(mw.getServerDir() +
- '/openresty/nginx/html/index.html')
- vlist.append(mw.getServerDir() + '/web_conf/stop/index.html')
- data = {}
- data['path'] = vlist[int(stype)]
- return mw.returnJson(True, 'ok', data)
-
- def addSiteTypeApi(self):
- name = request.form.get('name', '').strip()
- if not name:
- return mw.returnJson(False, "分类名称不能为空")
- if len(name) > 18:
- return mw.returnJson(False, "分类名称长度不能超过6个汉字或18位字母")
- if mw.M('site_types').count() >= 10:
- return mw.returnJson(False, '最多添加10个分类!')
- if mw.M('site_types').where('name=?', (name,)).count() > 0:
- return mw.returnJson(False, "指定分类名称已存在!")
- mw.M('site_types').add("name", (name,))
- return mw.returnJson(True, '添加成功!')
-
- def removeSiteTypeApi(self):
- mid = request.form.get('id', '')
- if mw.M('site_types').where('id=?', (mid,)).count() == 0:
- return mw.returnJson(False, "指定分类不存在!")
- mw.M('site_types').where('id=?', (mid,)).delete()
- mw.M("sites").where("type_id=?", (mid,)).save("type_id", (0,))
- return mw.returnJson(True, "分类已删除!")
-
- def modifySiteTypeNameApi(self):
- # 修改网站分类名称
- name = request.form.get('name', '').strip()
- mid = request.form.get('id', '')
- if not name:
- return mw.returnJson(False, "分类名称不能为空")
- if len(name) > 18:
- return mw.returnJson(False, "分类名称长度不能超过6个汉字或18位字母")
- if mw.M('site_types').where('id=?', (mid,)).count() == 0:
- return mw.returnJson(False, "指定分类不存在!")
- mw.M('site_types').where('id=?', (mid,)).setField('name', name)
- return mw.returnJson(True, "修改成功!")
-
- def setSiteTypeApi(self):
- # 设置指定站点的分类
- site_ids = request.form.get('site_ids', '')
- mid = request.form.get('id', '')
- site_ids = json.loads(site_ids)
- for sid in site_ids:
- print(mw.M('sites').where('id=?', (sid,)).setField('type_id', mid))
- return mw.returnJson(True, "设置成功!")
-
- ##### ----- end ----- ###
-
- # 域名编码转换
- def toPunycode(self, domain):
- import re
- if sys.version_info[0] == 2:
- domain = domain.encode('utf8')
- tmp = domain.split('.')
- newdomain = ''
- for dkey in tmp:
- # 匹配非ascii字符
- match = re.search(u"[\x80-\xff]+", dkey)
- if not match:
- newdomain += dkey + '.'
- else:
- newdomain += 'xn--' + \
- dkey.decode('utf-8').encode('punycode') + '.'
- return newdomain[0:-1]
-
- # 中文路径处理
- def toPunycodePath(self, path):
- if sys.version_info[0] == 2:
- path = path.encode('utf-8')
- if os.path.exists(path):
- return path
- import re
- match = re.search(u"[\x80-\xff]+", path)
- if not match:
- return path
- npath = ''
- for ph in path.split('/'):
- npath += '/' + self.toPunycode(ph)
- return npath.replace('//', '/')
-
- # 路径处理
- def getPath(self, path):
- if path[-1] == '/':
- return path[0:-1]
- return path
-
- def getSitePath(self, siteName):
- file = self.getHostConf(siteName)
- if os.path.exists(file):
- conf = mw.readFile(file)
- rep = r'\s*root\s*(.+);'
- find_cnf = re.search(rep, conf)
- if not find_cnf:
- return ''
- path = find_cnf.groups()[0]
- return path
- return ''
-
- # 取当站点前运行目录
- def getSiteRunPath(self, mid):
- siteName = mw.M('sites').where('id=?', (mid,)).getField('name')
- sitePath = mw.M('sites').where('id=?', (mid,)).getField('path')
- path = sitePath
-
- filename = self.getHostConf(siteName)
- if os.path.exists(filename):
- conf = mw.readFile(filename)
- rep = r'\s*root\s*(.+);'
- path = re.search(rep, conf).groups()[0]
-
- data = {}
- if sitePath == path:
- data['runPath'] = '/'
- else:
- data['runPath'] = path.replace(sitePath, '')
-
- dirnames = []
- dirnames.append('/')
- for filename in os.listdir(sitePath):
- try:
- filePath = sitePath + '/' + filename
- if os.path.islink(filePath):
- continue
- if os.path.isdir(filePath):
- dirnames.append('/' + filename)
- except:
- pass
-
- data['dirs'] = dirnames
- return data
-
- def getHostConf(self, siteName):
- return self.vhostPath + '/' + siteName + '.conf'
-
- def getRewriteConf(self, siteName):
- return self.rewritePath + '/' + siteName + '.conf'
-
- def getRedirectDataPath(self, siteName):
- return "{}/{}/data.json".format(self.redirectPath, siteName)
-
- def getRedirectPath(self, siteName):
- return "{}/{}".format(self.redirectPath, siteName)
-
- def getProxyDataPath(self, siteName):
- return "{}/{}/data.json".format(self.proxyPath, siteName)
-
- def getProxyPath(self, siteName):
- return "{}/{}".format(self.proxyPath, siteName)
-
- def getDirBindRewrite(self, siteName, dirname):
- return self.rewritePath + '/' + siteName + '_' + dirname + '.conf'
-
- def getIndexConf(self):
- return mw.getServerDir() + '/openresty/nginx/conf/nginx.conf'
-
- def getDomain(self, pid):
- _list = mw.M('domain').where("pid=?", (pid,)).field(
- 'id,pid,name,port,addtime').select()
- return mw.getJson(_list)
-
- def getLogs(self, siteName):
- logPath = mw.getLogsDir() + '/' + siteName + '.log'
- if not os.path.exists(logPath):
- return mw.returnJson(False, '日志为空')
- return mw.returnJson(True, mw.getLastLine(logPath, 100))
-
- def getErrorLogs(self, siteName):
- logPath = mw.getLogsDir() + '/' + siteName + '.error.log'
- if not os.path.exists(logPath):
- return mw.returnJson(False, '日志为空')
- return mw.returnJson(True, mw.getLastLine(logPath, 100))
-
- # 取日志状态
- def getLogsStatus(self, siteName):
- filename = self.getHostConf(siteName)
- conf = mw.readFile(filename)
- if conf.find('#ErrorLog') != -1:
- return False
- if conf.find("access_log off") != -1:
- return False
- return True
-
- # 取目录加密状态
- def getHasPwd(self, siteName):
- filename = self.getHostConf(siteName)
- conf = mw.readFile(filename)
- if conf.find('#AUTH_START') != -1:
- return True
- return False
-
- def getSitePhpVersion(self, siteName):
- conf = mw.readFile(self.getHostConf(siteName))
- rep = r"enable-php-(.*)\.conf"
- find_php_cnf = re.search(rep, conf)
-
- def_pver = '00'
- if find_php_cnf:
- tmp = find_php_cnf.groups()
- def_pver = tmp[0]
-
- data = {}
- data['phpversion'] = def_pver
- return mw.getJson(data)
-
- def getIndex(self, sid):
- siteName = mw.M('sites').where("id=?", (sid,)).getField('name')
- file = self.getHostConf(siteName)
- conf = mw.readFile(file)
- rep = r"\s+index\s+(.+);"
- tmp = re.search(rep, conf).groups()
- return tmp[0].replace(' ', ',')
-
- def setIndex(self, sid, index):
- if index.find('.') == -1:
- return mw.returnJson(False, '默认文档格式不正确,例:index.html')
-
- index = index.replace(' ', '')
- index = index.replace(',,', ',')
-
- if len(index) < 3:
- return mw.returnJson(False, '默认文档不能为空!')
-
- siteName = mw.M('sites').where("id=?", (sid,)).getField('name')
- index_l = index.replace(",", " ")
- file = self.getHostConf(siteName)
- conf = mw.readFile(file)
- if conf:
- rep = r"\s+index\s+.+;"
- conf = re.sub(rep, "\n\tindex " + index_l + ";", conf)
- mw.writeFile(file, conf)
-
- mw.writeLog('TYPE_SITE', 'SITE_INDEX_SUCCESS', (siteName, index_l))
- return mw.returnJson(True, '设置成功!')
-
- def getLimitNet(self, sid):
- siteName = mw.M('sites').where("id=?", (sid,)).getField('name')
- filename = self.getHostConf(siteName)
- # 站点总并发
- data = {}
- conf = mw.readFile(filename)
- try:
- rep = r"\s+limit_conn\s+perserver\s+([0-9]+);"
- tmp = re.search(rep, conf).groups()
- data['perserver'] = int(tmp[0])
-
- # IP并发限制
- rep = r"\s+limit_conn\s+perip\s+([0-9]+);"
- tmp = re.search(rep, conf).groups()
- data['perip'] = int(tmp[0])
-
- # 请求并发限制
- rep = r"\s+limit_rate\s+([0-9]+)\w+;"
- tmp = re.search(rep, conf).groups()
- data['limit_rate'] = int(tmp[0])
- except:
- data['perserver'] = 0
- data['perip'] = 0
- data['limit_rate'] = 0
-
- return mw.getJson(data)
-
- def checkIndexConf(self):
- limit = self.getIndexConf()
- nginxConf = mw.readFile(limit)
- limitConf = "limit_conn_zone $binary_remote_addr zone=perip:10m;\n\t\tlimit_conn_zone $server_name zone=perserver:10m;"
- nginxConf = nginxConf.replace(
- "#limit_conn_zone $binary_remote_addr zone=perip:10m;", limitConf)
- mw.writeFile(limit, nginxConf)
-
- def saveLimitNet(self, sid, perserver, perip, limit_rate):
-
- str_perserver = 'limit_conn perserver ' + perserver + ';'
- str_perip = 'limit_conn perip ' + perip + ';'
- str_limit_rate = 'limit_rate ' + limit_rate + 'k;'
-
- siteName = mw.M('sites').where("id=?", (sid,)).getField('name')
- filename = self.getHostConf(siteName)
-
- conf = mw.readFile(filename)
- if(conf.find('limit_conn perserver') != -1):
- # 替换总并发
- rep = r"limit_conn\s+perserver\s+([0-9]+);"
- conf = re.sub(rep, str_perserver, conf)
-
- # 替换IP并发限制
- rep = r"limit_conn\s+perip\s+([0-9]+);"
- conf = re.sub(rep, str_perip, conf)
-
- # 替换请求流量限制
- rep = r"limit_rate\s+([0-9]+)\w+;"
- conf = re.sub(rep, str_limit_rate, conf)
- else:
- conf = conf.replace('#error_page 404/404.html;', "#error_page 404/404.html;\n " +
- str_perserver + "\n " + str_perip + "\n " + str_limit_rate)
-
- mw.writeFile(filename, conf)
- mw.restartWeb()
- mw.writeLog('TYPE_SITE', 'SITE_NETLIMIT_OPEN_SUCCESS', (siteName,))
- return mw.returnJson(True, '设置成功!')
-
- def closeLimitNet(self, sid):
- siteName = mw.M('sites').where("id=?", (sid,)).getField('name')
- filename = self.getHostConf(siteName)
- conf = mw.readFile(filename)
- # 清理总并发
- rep = r"\s+limit_conn\s+perserver\s+([0-9]+);"
- conf = re.sub(rep, '', conf)
-
- # 清理IP并发限制
- rep = r"\s+limit_conn\s+perip\s+([0-9]+);"
- conf = re.sub(rep, '', conf)
-
- # 清理请求流量限制
- rep = r"\s+limit_rate\s+([0-9]+)\w+;"
- conf = re.sub(rep, '', conf)
- mw.writeFile(filename, conf)
- mw.restartWeb()
- mw.writeLog(
- 'TYPE_SITE', 'SITE_NETLIMIT_CLOSE_SUCCESS', (siteName,))
- return mw.returnJson(True, '已关闭流量限制!')
-
- def getSecurity(self, sid, name):
- filename = self.getHostConf(name)
- conf = mw.readFile(filename)
- data = {}
- if conf.find('SECURITY-START') != -1:
- rep = "#SECURITY-START(\n|.){1,500}#SECURITY-END"
- tmp = re.search(rep, conf).group()
- data['fix'] = re.search(
- r"\(.+\)\$", tmp).group().replace('(', '').replace(')$', '').replace('|', ',')
-
- data['status'] = False
- data['none'] = False
-
- valid_referers = re.search(
- r"valid_referers\s+(.+);\n", tmp)
- valid_referers_none = re.search(
- r"valid_referers\s+none\s+blocked\s+(.+);\n", tmp)
-
- if valid_referers or valid_referers_none:
- data['status'] = True
-
- if valid_referers_none:
- domain_t = valid_referers_none.groups()[0].split()
- data['domains'] = ','.join(domain_t)
- data['none'] = True
- elif valid_referers:
- domain_t = valid_referers.groups()[0].split()
- data['domains'] = ','.join(domain_t)
- data['none'] = False
-
- # print(data)
- else:
- data['fix'] = 'jpg,jpeg,gif,png,js,css'
- domains = mw.M('domain').where(
- 'pid=?', (sid,)).field('name').select()
- tmp = []
- for domain in domains:
- tmp.append(domain['name'])
- data['domains'] = ','.join(tmp)
- data['status'] = False
- data['none'] = False
- return mw.getJson(data)
-
- def setSecurity(self, sid, name, fix, domains, status, none=''):
- if len(fix) < 2:
- return mw.returnJson(False, 'URL后缀不能为空!')
- file = self.getHostConf(name)
- if os.path.exists(file):
- conf = mw.readFile(file)
- if status == 'false':
- rep = r"\s{0,4}#SECURITY-START(\n|.){1,500}#SECURITY-END\n?"
- conf = re.sub(rep, '', conf)
- mw.writeLog('网站管理', '站点[' + name + ']已关闭防盗链设置!')
- else:
- rep = r"\s{0,4}#SECURITY-START(\n|.){1,500}#SECURITY-END\n?"
- conf = re.sub(rep, '', conf)
-
- valid_referers = domains.strip().replace(',', ' ')
- if none == 'true':
- valid_referers = 'none blocked ' + valid_referers
-
- pre_path = self.setupPath + "/php/conf"
- re_path = r"include\s+" + pre_path + "/enable-php-"
- rconf = r'''#SECURITY-START 防盗链配置
- location ~ .*\.(%s)$
- {
- expires 30d;
- access_log /dev/null;
- valid_referers %s;
- if ($invalid_referer){
- return 404;
- }
- }
- #SECURITY-END
- include %s/enable-php-''' % (fix.strip().replace(',', '|'), valid_referers, pre_path)
- conf = re.sub(re_path, rconf, conf)
- mw.writeLog('网站管理', '站点[' + name + ']已开启防盗链!')
- mw.writeFile(file, conf)
- mw.restartWeb()
- return mw.returnJson(True, '设置成功!')
-
- def getPhpVersion(self):
- phpVersions = ('00', '52', '53', '54', '55',
- '56', '70', '71', '72', '73',
- '74', '80', '81', '82', '83',
- '84')
- data = []
- for val in phpVersions:
- tmp = {}
- if val == '00':
- tmp['version'] = '00'
- tmp['name'] = '纯静态'
- data.append(tmp)
-
- # 标准判断
- checkPath = mw.getServerDir() + '/php/' + val + '/bin/php'
- if os.path.exists(checkPath):
- tmp['version'] = val
- tmp['name'] = 'PHP-' + val
- data.append(tmp)
-
- # 其他PHP安装类型
- conf_dir = mw.getServerDir() + "/web_conf/php/conf"
- conf_list = os.listdir(conf_dir)
- l = len(conf_list)
- rep = r"enable-php-(.*?)\.conf"
- for name in conf_list:
- tmp = {}
- try:
- matchVer = re.search(rep, name).groups()[0]
- except Exception as e:
- continue
-
- if matchVer in phpVersions:
- continue
-
- tmp['version'] = matchVer
- tmp['name'] = 'PHP-' + matchVer
- data.append(tmp)
-
- return data
-
- # 是否跳转到https
- def isToHttps(self, siteName):
- file = self.getHostConf(siteName)
- conf = mw.readFile(file)
- if conf:
- # if conf.find('HTTP_TO_HTTPS_START') != -1:
- # return True
- if conf.find('$server_port !~ 443') != -1:
- return True
- return False
-
- def getRewriteList(self):
- rewriteList = {}
- rewriteList['rewrite'] = []
- rewriteList['rewrite'].append('0.当前')
- for ds in os.listdir('rewrite/nginx'):
- rewriteList['rewrite'].append(ds[0:len(ds) - 5])
- rewriteList['rewrite'] = sorted(rewriteList['rewrite'])
- return rewriteList
-
- def createRootDir(self, path):
- autoInit = False
- if not os.path.exists(path):
- autoInit = True
- os.makedirs(path)
- if not mw.isAppleSystem():
- mw.execShell('chown -R www:www ' + path)
-
- if autoInit:
- mw.writeFile(path + '/index.html', 'Work has started!!!')
- mw.execShell('chmod -R 755 ' + path)
-
- def nginxAddDomain(self, webname, domain, port):
- file = self.getHostConf(webname)
- conf = mw.readFile(file)
- if not conf:
- return
-
- # 添加域名
- rep = r"server_name\s*(.*);"
- tmp = re.search(rep, conf).group()
- domains = tmp.split(' ')
- if not mw.inArray(domains, domain):
- newServerName = tmp.replace(';', ' ' + domain + ';')
- conf = conf.replace(tmp, newServerName)
-
- # 添加端口
- rep = r"listen\s+([0-9]+)\s*[default_server]*\s*;"
- tmp = re.findall(rep, conf)
- if not mw.inArray(tmp, port):
- listen = re.search(rep, conf).group()
- conf = conf.replace(
- listen, listen + "\n\tlisten " + port + ';')
- # 保存配置文件
- mw.writeFile(file, conf)
- return True
-
- def nginxAddConf(self):
- source_tpl = mw.getRunDir() + '/data/tpl/nginx.conf'
- vhost_file = self.vhostPath + '/' + self.siteName + '.conf'
- content = mw.readFile(source_tpl)
-
- content = content.replace('{$PORT}', self.sitePort)
- content = content.replace('{$SERVER_NAME}', self.siteName)
- content = content.replace('{$ROOT_DIR}', self.sitePath)
- content = content.replace('{$PHP_DIR}', self.setupPath + '/php')
- content = content.replace('{$PHPVER}', self.phpVersion)
- content = content.replace('{$OR_REWRITE}', self.rewritePath)
- # content = content.replace('{$OR_REDIRECT}', self.redirectPath)
- # content = content.replace('{$OR_PROXY}', self.proxyPath)
-
- logsPath = mw.getLogsDir()
- content = content.replace('{$LOGPATH}', logsPath)
- mw.writeFile(vhost_file, content)
-
-# 和反代配置冲突 && 默认伪静态为空
-# rewrite_content = '''
-# location /{
-# if ($PHP_ENV != "1"){
-# break;
-# }
-
-# if (!-e $request_filename) {
-# rewrite ^(.*)$ /index.php/$1 last;
-# break;
-# }
-# }
-# '''
- rewrite_file = self.rewritePath + '/' + self.siteName + '.conf'
- mw.writeFile(rewrite_file, '')
-
- def add(self, webname, port, ps, path, version):
- site_root_dir = mw.getWwwDir()
- if site_root_dir == path.rstrip('/'):
- return mw.returnJson(False, '不要以网站根目录创建站点!')
-
- siteMenu = json.loads(webname)
- self.siteName = self.toPunycode(siteMenu['domain'].strip().split(':')[0]).strip()
- self.sitePath = self.toPunycodePath(self.getPath(path.replace(' ', '')))
- self.sitePort = port.strip().replace(' ', '')
- self.phpVersion = version
-
- if mw.M('sites').where("name=?", (self.siteName,)).count():
- return mw.returnJson(False, '您添加的站点已存在!')
-
- # 写入数据库
- pid = mw.M('sites').add('name,path,status,ps,edate,addtime,type_id',
- (self.siteName, self.sitePath, '1', ps, '0000-00-00', mw.getDate(), 0,))
- opid = mw.M('domain').where("name=?", (self.siteName,)).getField('pid')
- if opid:
- if mw.M('sites').where('id=?', (opid,)).count():
- return mw.returnJson(False, '您添加的域名已存在!')
- mw.M('domain').where('pid=?', (opid,)).delete()
-
- self.createRootDir(self.sitePath)
- self.nginxAddConf()
-
- mw.M('domain').add('pid,name,port,addtime',
- (pid, self.siteName, self.sitePort, mw.getDate()))
-
- # 添加更多域名
- for domain in siteMenu['domainlist']:
- self.addDomain(domain, self.siteName, pid)
-
- data = {}
- data['siteStatus'] = False
- mw.restartWeb()
-
- self.runHook('site_cb', 'add')
- return mw.returnJson(True, '添加成功')
-
- def deleteWSLogs(self, webname):
- assLogPath = mw.getLogsDir() + '/' + webname + '.log'
- errLogPath = mw.getLogsDir() + '/' + webname + '.error.log'
- confFile = self.setupPath + '/nginx/vhost/' + webname + '.conf'
- rewriteFile = self.setupPath + '/nginx/rewrite/' + webname + '.conf'
- passFile = self.setupPath + '/nginx/pass/' + webname + '.conf'
- keyPath = self.sslDir + webname + '/privkey.pem'
- certPath = self.sslDir + webname + '/fullchain.pem'
- logs = [assLogPath,
- errLogPath,
- confFile,
- rewriteFile,
- passFile,
- keyPath,
- certPath]
- for i in logs:
- mw.deleteFile(i)
-
- # 重定向目录
- redirectDir = self.setupPath + '/nginx/redirect/' + webname
- if os.path.exists(redirectDir):
- mw.execShell('rm -rf ' + redirectDir)
- # 代理目录
- proxyDir = self.setupPath + '/nginx/proxy/' + webname
- if os.path.exists(proxyDir):
- mw.execShell('rm -rf ' + proxyDir)
-
- def delete(self, sid, webname, path):
- self.deleteWSLogs(webname)
- if path == '1':
- rootPath = mw.getWwwDir() + '/' + webname
- mw.execShell('rm -rf ' + rootPath)
-
- # ssl
- ssl_dir = self.sslDir + '/' + webname
- if os.path.exists(ssl_dir):
- mw.execShell('rm -rf ' + ssl_dir)
-
- ssl_lets_dir = self.sslLetsDir + '/' + webname
- if os.path.exists(ssl_lets_dir):
- mw.execShell('rm -rf ' + ssl_lets_dir)
-
- ssl_acme_dir = mw.getAcmeDir() + '/' + webname
- if os.path.exists(ssl_acme_dir):
- mw.execShell('rm -rf ' + ssl_acme_dir)
-
- mw.M('sites').where("id=?", (sid,)).delete()
- mw.M('domain').where("pid=?", (sid,)).delete()
- mw.M('domain').where("name=?", (webname,)).delete()
-
- # binding domain delete
- binding_list = mw.M('binding').field(
- 'id,domain').where("pid=?", (sid,)).select()
-
- for x in binding_list:
- wlog = mw.getLogsDir() + "/" + webname + "_" + x['domain'] + ".log"
- wlog_error = mw.getLogsDir() + "/" + webname + "_" + \
- x['domain'] + ".error.log"
-
- if os.path.exists(wlog):
- mw.execShell('rm -rf ' + wlog)
- if os.path.exists(wlog_error):
- mw.execShell('rm -rf ' + wlog_error)
-
- mw.M('binding').where("pid=?", (sid,)).delete()
- mw.restartWeb()
-
- self.runHook('site_cb', 'delete')
- return mw.returnJson(True, '站点删除成功!')
-
- def setEndDate(self, sid, edate):
- result = mw.M('sites').where(
- 'id=?', (sid,)).setField('edate', edate)
- siteName = mw.M('sites').where('id=?', (sid,)).getField('name')
- mw.writeLog('TYPE_SITE', '设置成功,站点到期后将自动停止!', (siteName, edate))
- return mw.returnJson(True, '设置成功,站点到期后将自动停止!')
-
- # ssl相关方法 start
- def setSslConf(self, siteName):
- file = self.getHostConf(siteName)
- conf = mw.readFile(file)
-
- version = ''
- version_file_pl = mw.getServerDir() + '/openresty/version.pl'
- if os.path.exists(version_file_pl):
- version = mw.readFile(version_file_pl)
- version = version.strip()
-
-
- keyPath = self.sslDir + '/' + siteName + '/privkey.pem'
- certPath = self.sslDir + '/' + siteName + '/fullchain.pem'
- if conf:
- if conf.find('ssl_certificate') == -1:
- # ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
- # add_header Alt-Svc 'h3=":443";ma=86400,h3-29=":443";ma=86400';
- http3Header = """
- add_header Strict-Transport-Security "max-age=63072000";
- add_header Alt-Svc 'h3=":443";ma=86400';
-"""
- if not version.startswith('1.25'):
- http3Header = '';
-
- sslStr = """#error_page 404/404.html;
- ssl_certificate %s;
- ssl_certificate_key %s;
- ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
- ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
- ssl_prefer_server_ciphers on;
- ssl_session_cache shared:SSL:10m;
- ssl_session_timeout 10m;
- %s
- error_page 497 https://$host$request_uri;""" % (certPath, keyPath, http3Header)
- if(conf.find('ssl_certificate') != -1):
- return mw.returnData(True, 'SSL开启成功!')
-
- conf = conf.replace('#error_page 404/404.html;', sslStr)
-
- rep = r"listen\s+([0-9]+)\s*[default_server|reuseport]*;"
- tmp = re.findall(rep, conf)
- if not mw.inArray(tmp, '443'):
- listen = re.search(rep, conf).group()
-
- if version.startswith('1.25'):
- http_ssl = "\n\tlisten 443 ssl;"
- http_ssl = http_ssl + "\n\tlisten [::]:443 ssl;"
- http_ssl = http_ssl + "\n\tlisten 443 quic;"
- http_ssl = http_ssl + "\n\tlisten [::]:443 quic;"
- http_ssl = http_ssl + "\n\thttp2 on;"
- else:
- http_ssl = "\n\tlisten 443 ssl http2;"
- http_ssl = http_ssl + "\n\tlisten [::]:443 ssl http2;"
-
-
- conf = conf.replace(listen, listen + http_ssl)
-
- mw.backFile(file)
- mw.writeFile(file, conf)
- isError = mw.checkWebConfig()
- if(isError != True):
- mw.restoreFile(file)
- return mw.returnData(False, '证书错误:
' + isError.replace("\n", '
') + '')
-
- self.saveCert(keyPath, certPath)
-
- msg = mw.getInfo('网站[{1}]开启SSL成功!', (siteName,))
- mw.writeLog('网站管理', msg)
-
- mw.restartWeb()
- return mw.returnData(True, 'SSL开启成功!')
-
- def saveCert(self, keyPath, certPath):
- try:
- certInfo = mw.getCertName(certPath)
- if not certInfo:
- return mw.returnData(False, '证书解析失败!')
- vpath = self.sslDir + '/' + certInfo['subject'].strip()
- if not os.path.exists(vpath):
- os.system('mkdir -p ' + vpath)
- mw.writeFile(vpath + '/privkey.pem', mw.readFile(keyPath))
- mw.writeFile(vpath + '/fullchain.pem', mw.readFile(certPath))
- mw.writeFile(vpath + '/info.json', json.dumps(certInfo))
- return mw.returnData(True, '证书保存成功!')
- except Exception as e:
- return mw.returnData(False, '证书保存失败!')
-
- # 清除多余user.ini
- def delUserInI(self, path, up=0):
- for p1 in os.listdir(path):
- try:
- npath = path + '/' + p1
- if os.path.isdir(npath):
- if up < 100:
- self.delUserInI(npath, up + 1)
- else:
- continue
- useriniPath = npath + '/.user.ini'
- if not os.path.exists(useriniPath):
- continue
- mw.execShell('which chattr && chattr -i ' + useriniPath)
- mw.execShell('rm -f ' + useriniPath)
- except:
- continue
- return True
-
- # 设置目录防御
- def setDirUserINI(self, sitePath, runPath):
- newPath = sitePath + runPath
-
- filename = newPath + '/.user.ini'
- if os.path.exists(filename):
- mw.execShell("chattr -i " + filename)
- os.remove(filename)
- return mw.returnJson(True, '已清除防跨站设置!')
-
- self.delUserInI(newPath)
- openPath = 'open_basedir={}/:{}/'.format(newPath, sitePath)
- if runPath == '/':
- openPath = 'open_basedir={}/'.format(sitePath)
-
- mw.writeFile(filename, openPath + ':/www/server/php:/tmp/:/proc/')
- mw.execShell("chattr +i " + filename)
-
- return mw.returnJson(True, '已打开防跨站设置!')
-# ssl相关方法 end
diff --git a/class/core/system_api.py b/class/core/system_api.py
deleted file mode 100755
index 12208a35f..000000000
--- a/class/core/system_api.py
+++ /dev/null
@@ -1,978 +0,0 @@
-# coding: utf-8
-
-# ---------------------------------------------------------------------------------
-# MW-Linux面板
-# ---------------------------------------------------------------------------------
-# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
-# ---------------------------------------------------------------------------------
-# Author: midoks
")
- return mw.returnJson(True, '更新信息!', data)
-
- if stype == 'update':
- if version == '':
- return mw.returnJson(False, '缺少版本信息!')
-
- if new_ver != version:
- return mw.returnJson(False, '更新失败,请重试!')
-
- toPath = mw.getRootDir() + '/temp'
- if not os.path.exists(toPath):
- mw.execShell('mkdir -p ' + toPath)
-
- newUrl = "https://github.com/midoks/mdserver-web/archive/refs/tags/" + version + ".zip"
-
- dist_mw = toPath + '/mw.zip'
- if not os.path.exists(dist_mw):
- mw.execShell(
- 'wget --no-check-certificate -O ' + dist_mw + ' ' + newUrl)
-
- dist_to = toPath + "/mdserver-web-" + version
- if not os.path.exists(dist_to):
- os.system('unzip -o ' + toPath +
- '/mw.zip' + ' -d ' + toPath)
-
- cmd_cp = 'cp -rf ' + toPath + '/mdserver-web-' + \
- version + '/* ' + mw.getServerDir() + '/mdserver-web'
- mw.execShell(cmd_cp)
-
- mw.execShell('rm -rf ' + toPath + '/mdserver-web-' + version)
- mw.execShell('rm -rf ' + toPath + '/mw.zip')
-
- update_env = '''
-#!/bin/bash
-PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
-
-P_VER=`python3 -V | awk '{print $2}'`
-
-if [ ! -f /www/server/mdserver-web/bin/activate ];then
- cd /www/server/mdserver-web && python3 -m venv .
- cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate
-else
- cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate
-fi
-
-cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
-PIPSRC="https://pypi.python.org/simple"
-if [ ! -z "$cn" ];then
- PIPSRC="https://pypi.tuna.tsinghua.edu.cn/simple"
-fi
-
-cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/requirements.txt -i $PIPSRC
-
-P_VER_D=`echo "$P_VER"|awk -F '.' '{print $1}'`
-P_VER_M=`echo "$P_VER"|awk -F '.' '{print $2}'`
-NEW_P_VER=${P_VER_D}.${P_VER_M}
-
-if [ -f /www/server/mdserver-web/version/r${NEW_P_VER}.txt ];then
- cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/version/r${NEW_P_VER}.txt -i $PIPSRC
-fi
-'''
- os.system(update_env)
- self.restartMw()
- return mw.returnJson(True, '安装更新成功!')
-
- return mw.returnJson(False, '已经是最新,无需更新!')
- except Exception as ex:
- # print('updateServer', ex)
- return mw.returnJson(False, "连接服务器失败!" + str(ex))
-
- # 修复面板
- def repPanel(self, get):
- vp = ''
- if mw.readFile('/www/server/mdserver-web/class/common.py').find('checkSafe') != -1:
- vp = '_pro'
- mw.ExecShell("wget -O update.sh " + mw.get_url() +
- "/install/update" + vp + ".sh && bash update.sh")
- if hasattr(web.ctx.session, 'getCloudPlugin'):
- del(web.ctx.session['getCloudPlugin'])
- return True
diff --git a/class/core/task_api.py b/class/core/task_api.py
deleted file mode 100755
index 19d485e02..000000000
--- a/class/core/task_api.py
+++ /dev/null
@@ -1,103 +0,0 @@
-# coding: utf-8
-
-# ---------------------------------------------------------------------------------
-# MW-Linux面板
-# ---------------------------------------------------------------------------------
-# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
-# ---------------------------------------------------------------------------------
-# Author: midoks ';
+ readme += '
';
diff --git a/plugins/backup_ftp/class/ftp_client.py b/plugins/backup_ftp/class/ftp_client.py
index 97df92e32..53a0e7ea8 100644
--- a/plugins/backup_ftp/class/ftp_client.py
+++ b/plugins/backup_ftp/class/ftp_client.py
@@ -14,8 +14,12 @@ import json
import paramiko
import ftplib
-sys.path.append(os.getcwd() + "/class/core")
-import mw
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
DEBUG = True
BLOCK_SIZE = 1024 * 1024 * 2
@@ -136,7 +140,7 @@ class FtpPSClient:
"database": "db",
"path": "path",
}
- file_regx = prefix_dict.get(data_type) + "_(.+)_20\d+_\d+\."
+ file_regx = prefix_dict.get(data_type) + r"_(.+)_20\d+_\d+\."
sub_search = re.search(file_regx, file_name)
sub_path_name = ""
if sub_search:
diff --git a/plugins/backup_ftp/index.py b/plugins/backup_ftp/index.py
index 34dd3f959..9b292b0db 100755
--- a/plugins/backup_ftp/index.py
+++ b/plugins/backup_ftp/index.py
@@ -12,9 +12,13 @@ if sys.platform != "darwin":
os.chdir("/www/server/mdserver-web")
-sys.path.append(os.getcwd() + "/class/core")
-import mw
-import db
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
+import core.db as db
_ver = sys.version_info
is_py2 = (_ver[0] == 2)
@@ -184,7 +188,6 @@ def findPathName(path, filename):
return l
def backupAllFunc(stype):
- os.chdir(mw.getRunDir())
name = sys.argv[2]
num = sys.argv[3]
@@ -196,20 +199,15 @@ def backupAllFunc(stype):
}
backups = []
- sql = db.Sql()
-
# print("stype:", stype)
# 提前获取-清理多余备份
if stype == 'site':
- pid = sql.table('sites').where('name=?', (name,)).getField('id')
- backups = sql.table('backup').where(
- 'type=? and pid=?', ('0', pid)).field('id,filename').select()
+ pid = mw.M('sites').where('name=?', (name,)).getField('id')
+ backups = mw.M('backup').where('type=? and pid=?', ('0', pid)).field('id,filename').select()
if stype == 'database':
db_path = mw.getServerDir() + '/mysql'
- pid = mw.M('databases').dbPos(db_path, 'mysql').where(
- 'name=?', (name,)).getField('id')
- backups = sql.table('backup').where(
- 'type=? and pid=?', ('1', pid)).field('id,filename').select()
+ pid = mw.M('databases').dbPos(db_path, 'mysql').where('name=?', (name,)).getField('id')
+ backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select()
if stype == 'path':
backup_dir = mw.getBackupDir()
backup_path = backup_dir + '/path'
@@ -220,18 +218,15 @@ def backupAllFunc(stype):
if stype.find('database_') > -1:
plugin_name = stype.replace('database_', '')
db_path = mw.getServerDir() + '/' + plugin_name
- pid = mw.M('databases').dbPos(db_path, 'mysql').where(
- 'name=?', (name,)).getField('id')
- backups = sql.table('backup').where(
- 'type=? and pid=?', ('1', pid)).field('id,filename').select()
+ pid = mw.M('databases').dbPos(db_path, 'mysql').where('name=?', (name,)).getField('id')
+ backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select()
args = stype + " " + name + " " + num
- cmd = 'python3 ' + mw.getRunDir() + '/scripts/backup.py ' + args
+ cmd = 'python3 ' + mw.getPanelDir() + '/scripts/backup.py ' + args
if stype.find('database_') > -1:
plugin_name = stype.replace('database_', '')
args = "database " + name + " " + num
- cmd = 'python3 ' + mw.getRunDir() + '/plugins/' + plugin_name + \
- '/scripts/backup.py ' + args
+ cmd = 'python3 ' + mw.getPanelDir() + '/plugins/' + plugin_name + '/scripts/backup.py ' + args
os.system(cmd)
@@ -251,8 +246,7 @@ def backupAllFunc(stype):
_name = 'path_{}'.format(os.path.basename(name))
find_path = mw.getBackupDir() + '/path/'+_name
- find_new_file = "ls " + find_path + \
- "_* | grep '.gz' | cut -d \ -f 1 | awk 'END {print}'"
+ find_new_file = "ls " + find_path + "_* | grep '.gz' | cut -d \\ -f 1 | awk 'END {print}'"
filename = mw.execShell(find_new_file)[0].strip()
if filename == "":
diff --git a/plugins/backup_ftp/install.sh b/plugins/backup_ftp/install.sh
index 12f3a5b8c..9d37cbf5d 100755
--- a/plugins/backup_ftp/install.sh
+++ b/plugins/backup_ftp/install.sh
@@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
-install_tmp=${rootPath}/tmp/mw_install.pl
sys_os=`uname`
@@ -15,7 +14,7 @@ Install_App()
{
mkdir -p ${serverPath}/backup_ftp
echo "${1}" > ${serverPath}/backup_ftp/version.pl
- echo '安装完成' > $install_tmp
+ echo '安装完成'
}
diff --git a/plugins/clean/index.py b/plugins/clean/index.py
index d9c29d5ed..d6d59b36c 100755
--- a/plugins/clean/index.py
+++ b/plugins/clean/index.py
@@ -5,8 +5,12 @@ import io
import os
import time
-sys.path.append(os.getcwd() + "/class/core")
-import mw
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
app_debug = False
if mw.isAppleSystem():
diff --git a/plugins/clean/install.sh b/plugins/clean/install.sh
index c8cfe95d5..d52b1c1c2 100755
--- a/plugins/clean/install.sh
+++ b/plugins/clean/install.sh
@@ -10,7 +10,6 @@ rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
-install_tmp=${rootPath}/tmp/mw_install.pl
VERSION=$2
if [ -f ${rootPath}/bin/activate ];then
@@ -20,19 +19,19 @@ fi
Install_app()
{
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
mkdir -p $serverPath/clean
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py start
echo "${VERSION}" > $serverPath/clean/version.pl
- echo '安装完成' > $install_tmp
+ echo '安装完成'
}
Uninstall_app()
{
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py stop
rm -rf $serverPath/clean
- echo "Uninstall_clean" > $install_tmp
+ echo "Uninstall_clean"
}
action=$1
diff --git a/plugins/clean/tool_task.py b/plugins/clean/tool_task.py
index bf9654e2d..cc762f7c9 100644
--- a/plugins/clean/tool_task.py
+++ b/plugins/clean/tool_task.py
@@ -6,8 +6,13 @@ import os
import time
import json
-sys.path.append(os.getcwd() + "/class/core")
-import mw
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
+from utils.crontab import crontab as MwCrontab
app_debug = False
@@ -33,10 +38,9 @@ def getTaskConf():
def getConfigData():
- try:
+ conf = getTaskConf()
+ if os.path.exists(conf):
return json.loads(mw.readFile(getTaskConf()))
- except:
- pass
return {
"task_id": -1,
"period": "day-n",
@@ -64,10 +68,8 @@ def createBgTaskByName(name):
if res and res["id"] == args["task_id"]:
print("计划任务已经存在!")
return True
- import crontab_api
- api = crontab_api.crontab_api()
- mw_dir = mw.getRunDir()
+ mw_dir = mw.getPanelDir()
cmd = '''
mw_dir=%s
rname=%s
@@ -94,10 +96,10 @@ logs_file=$plugin_path/${rname}.log
'stype': "toShell",
'sname': '',
'sbody': cmd,
- 'urladdress': '',
+ 'url_address': '',
}
- task_id = api.add(params)
+ task_id = MwCrontab.instance().add(params)
if task_id > 0:
args["task_id"] = task_id
args["name"] = name
@@ -110,9 +112,7 @@ def removeBgTask():
res = mw.M("crontab").field("id, name").where(
"id=?", (cfg["task_id"],)).find()
if res and res["id"] == cfg["task_id"]:
- import crontab_api
- api = crontab_api.crontab_api()
- data = api.delete(cfg["task_id"])
+ data = MwCrontab.instance().delete(cfg["task_id"])
if data[0]:
cfg["task_id"] = -1
mw.writeFile(getTaskConf(), json.dumps(cfg))
diff --git a/plugins/cloudreve/index.py b/plugins/cloudreve/index.py
index a312a2028..d01cf0865 100755
--- a/plugins/cloudreve/index.py
+++ b/plugins/cloudreve/index.py
@@ -6,8 +6,12 @@ import os
import time
import re
-sys.path.append(os.getcwd() + "/class/core")
-import mw
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
app_debug = False
if mw.isAppleSystem():
@@ -103,7 +107,7 @@ def status():
def contentReplace(content):
service_path = mw.getServerDir()
- content = content.replace('{$ROOT_PATH}', mw.getRootDir())
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName())
return content
@@ -111,7 +115,7 @@ def contentReplace(content):
def initDreplace():
file_tpl = getInitDTpl()
- service_path = os.path.dirname(os.getcwd())
+ service_path = mw.getServerDir()
initD_path = getServerDir() + '/init.d'
if not os.path.exists(initD_path):
diff --git a/plugins/cloudreve/install.sh b/plugins/cloudreve/install.sh
index 0de5e44ef..11ac5d927 100755
--- a/plugins/cloudreve/install.sh
+++ b/plugins/cloudreve/install.sh
@@ -10,7 +10,6 @@ serverPath=$(dirname "$rootPath")
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/cloudreve && bash install.sh install 3.8.3
# cd /www/server/mdserver-web/plugins/cloudreve && bash install.sh install 3.8.3
-install_tmp=${rootPath}/tmp/mw_install.pl
VERSION=$2
sysArch=`arch`
diff --git a/plugins/data_query/index.py b/plugins/data_query/index.py
index 9bc0dd431..0d8061b61 100755
--- a/plugins/data_query/index.py
+++ b/plugins/data_query/index.py
@@ -6,8 +6,12 @@ import os
import time
import re
-sys.path.append(os.getcwd() + "/class/core")
-import mw
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
app_debug = False
if mw.isAppleSystem():
diff --git a/plugins/data_query/install.sh b/plugins/data_query/install.sh
index b3b580f17..7a45a82a2 100755
--- a/plugins/data_query/install.sh
+++ b/plugins/data_query/install.sh
@@ -7,12 +7,11 @@ rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
-install_tmp=${rootPath}/tmp/mw_install.pl
VERSION=$2
Install_App()
{
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
mkdir -p $serverPath/source
mkdir -p $serverPath/data_query
echo "${VERSION}" > $serverPath/data_query/version.pl
diff --git a/plugins/data_query/nosql_memcached.py b/plugins/data_query/nosql_memcached.py
index 4b392aad8..b32ef5690 100755
--- a/plugins/data_query/nosql_memcached.py
+++ b/plugins/data_query/nosql_memcached.py
@@ -11,11 +11,12 @@ import re
import json
import pymemcache
-try:
- sys.path.append(os.getcwd() + "/class/core")
- import mw
-except Exception as e:
- import core.mw as mw
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
def singleton(cls):
@@ -68,7 +69,7 @@ class nosqlMemcached():
if not mem_content: return False
keys = ["bind", "PORT"]
- rep = 'PORT\s*=\s*(.*)'
+ rep = r'PORT\s*=\s*(.*)'
port_re = re.search(rep, mem_content)
if port_re:
result['port'] = int(port_re.groups()[0].strip())
@@ -105,7 +106,7 @@ class nosqlMemcachedCtr():
item_no = []
for i in m_items:
- item_match = b'items:(\d*?):number'
+ item_match = rb'items:(\d*?):number'
item_match_re = re.search(item_match, i)
if item_match_re:
v = item_match_re.groups()[0].strip()
diff --git a/plugins/data_query/nosql_mongodb.py b/plugins/data_query/nosql_mongodb.py
index 286c63e45..9b22b9118 100755
--- a/plugins/data_query/nosql_mongodb.py
+++ b/plugins/data_query/nosql_mongodb.py
@@ -12,11 +12,12 @@ import yaml
from bson.objectid import ObjectId
from bson.json_util import dumps
-try:
- sys.path.append(os.getcwd() + "/class/core")
- import mw
-except Exception as e:
- import core.mw as mw
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
def singleton(cls):
_instance = {}
diff --git a/plugins/data_query/nosql_redis.py b/plugins/data_query/nosql_redis.py
index 65eb675e5..e9935f211 100755
--- a/plugins/data_query/nosql_redis.py
+++ b/plugins/data_query/nosql_redis.py
@@ -7,16 +7,12 @@ import time
import re
import redis
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
-try:
- sys.path.append(os.getcwd() + "/class/core")
- import mw
-except Exception as e:
- import core.mw as mw
-
-
-# def getPluginName():
-# return 'data_query'
+import core.mw as mw
def singleton(cls):
_instance = {}
@@ -47,7 +43,8 @@ class nosqlRedis():
if self.__DB_HOST in ['127.0.0.1', 'localhost']:
redis_path = "{}/redis".format(mw.getServerDir())
- if not os.path.exists(redis_path): return False
+ if not os.path.exists(redis_path):
+ return False
if not self.__DB_LOCAL:
self.__DB_PASS = self.__config['requirepass']
@@ -75,7 +72,7 @@ class nosqlRedis():
keys = ["bind", "port", "timeout", "maxclients", "databases", "requirepass", "maxmemory"]
for k in keys:
v = ""
- rep = "\n%s\s+(.+)" % k
+ rep = r"\n%s\s+(.+)" % k
group = re.search(rep, redis_conf)
if not group:
if k == "maxmemory":
diff --git a/plugins/data_query/sql_mysql.py b/plugins/data_query/sql_mysql.py
index ee8c83fbf..6179f00e3 100755
--- a/plugins/data_query/sql_mysql.py
+++ b/plugins/data_query/sql_mysql.py
@@ -8,11 +8,7 @@ import re
import pymongo
import json
-try:
- sys.path.append(os.getcwd() + "/class/core")
- import mw
-except Exception as e:
- import core.mw as mw
+import core.mw as mw
def singleton(cls):
_instance = {}
@@ -38,24 +34,18 @@ class nosqlMySQL():
__DB_LOCAL = None
def __init__(self):
- self.__config = self.get_options(None)
-
+ pass
+
+ def setSid(self, sid):
+ self.__config = self.get_options(sid=sid)
def conn(self):
-
- if self.__DB_HOST in ['127.0.0.1', 'localhost']:
- my_path = "{}/mysql".format(mw.getServerDir())
- if not os.path.exists(my_path): return False
-
- if not self.__DB_LOCAL:
- # print(self.__config)
- self.__DB_PORT = int(self.__config['port'])
- self.__DB_USER = self.__config['username']
- self.__DB_PASS = self.__config['password']
- self.__DB_SOCKET = self.__config['socket']
+ self.__DB_PORT = int(self.__config['port'])
+ self.__DB_USER = self.__config['username']
+ self.__DB_PASS = self.__config['password']
+ self.__DB_SOCKET = self.__config['socket']
try:
-
db = mw.getMyORM()
db.setPort(self.__DB_PORT)
db.setPwd(self.__DB_PASS)
@@ -68,42 +58,37 @@ class nosqlMySQL():
self.__DB_ERR = mw.get_error_info()
return False
- def sqliteDb(self,dbname='databases'):
- my_root_path = mw.getServerDir() +'/mysql'
+ def sqliteDb(self, db_pos_name, dbname='databases'):
+ mydb_path = mw.getServerDir() +'/'+db_pos_name
name = 'mysql'
- conn = mw.M(dbname).dbPos(my_root_path, name)
+ conn = mw.M(dbname).dbPos(mydb_path, name)
return conn
# 获取配置项
- def get_options(self, get=None):
+ def get_options(self, sid=None):
result = {}
-
-
- my_cnf_path = "{}/mysql/etc/my.cnf".format(mw.getServerDir())
- my_content = mw.readFile(my_cnf_path)
- if not my_content: return False
-
- mysql_pass = self.sqliteDb('config').where('id=?', (1,)).getField('mysql_root')
- result['password'] = mysql_pass
- result['username'] = 'root'
- keys = ["bind_ip", "port"]
-
+ result['socket'] = ''
+ result['port'] = 3306
result['host'] = '127.0.0.1'
- rep = 'port\s*=\s*(.*)'
+ result['username'] = 'root'
- port_re = re.search(rep, my_content)
- if port_re:
- result['port'] = int(port_re.groups()[0].strip())
- else:
- result['port'] = 3306
+ if sid in ['mysql', 'mysql-apt', 'mysql-yum']:
+ my_cnf_path = "{}/{}/etc/my.cnf".format(mw.getServerDir(),sid)
- socket_rep = 'socket\s*=\s*(.*)'
- socket_re = re.search(socket_rep, my_content)
- if socket_re:
- result['socket'] = socket_re.groups()[0].strip()
- else:
- result['socket'] = ''
+ mydb_content = mw.readFile(my_cnf_path)
+ if not mydb_content: return False
+ mysql_pass = self.sqliteDb(sid, 'config').where('id=?', (1,)).getField('mysql_root')
+ result['password'] = mysql_pass
+
+ rep = r'port\s*=\s*(.*)'
+ port_re = re.search(rep, mydb_content)
+ if port_re:
+ result['port'] = int(port_re.groups()[0].strip())
+ socket_rep = r'socket\s*=\s*(.*)'
+ socket_re = re.search(socket_rep, mydb_content)
+ if socket_re:
+ result['socket'] = socket_re.groups()[0].strip()
return result
def set_host(self, host, port, name, username, password, prefix=''):
@@ -118,7 +103,22 @@ class nosqlMySQL():
self.__DB_LOCAL = 1
return self
+ def getServerList(self):
+ my_cnf_path = "{}/mysql/etc/my.cnf".format(mw.getServerDir())
+ data = []
+ local_mysql = "{}/mysql/etc/my.cnf".format(mw.getServerDir())
+ if os.path.exists(local_mysql):
+ data.append({'name':'本地服务器', 'val':'mysql'})
+
+ local_mysql_apt = "{}/mysql-apt/etc/my.cnf".format(mw.getServerDir())
+ if os.path.exists(local_mysql_apt):
+ data.append({'name':'本地服务器[apt]', 'val':'mysql-apt'})
+
+ local_mysql_yum = "{}/mysql-yum/etc/my.cnf".format(mw.getServerDir())
+ if os.path.exists(local_mysql_yum):
+ data.append({'name':'本地服务器[yum]', 'val':'mysql-yum'})
+ return mw.returnData(True, 'ok', data)
@singleton
class nosqlMySQLCtr():
@@ -126,8 +126,13 @@ class nosqlMySQLCtr():
def __init__(self):
pass
- def getInstanceBySid(self, sid = 0):
+ def getServerList(self, args):
instance = nosqlMySQL()
+ return instance.getServerList()
+
+ def getInstanceBySid(self, sid):
+ instance = nosqlMySQL()
+ instance.setSid(sid)
return instance
def getDbList(self, args):
@@ -280,7 +285,372 @@ class nosqlMySQLCtr():
rdata['list'] = result
return mw.returnData(True,'ok', rdata)
+ def getNetRow(self, my_instance):
+ row = {}
+
+ data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Com_select'")
+ row['select'] = data['Value']
+
+ data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Com_insert'")
+ row['insert'] = data['Value']
+
+ data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Com_update'")
+ row['update'] = data['Value']
+
+ data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Com_delete'")
+ row['delete'] = data['Value']
+
+
+
+ data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Bytes_received'")
+ row['recv_bytes'] = data['Value']
+
+ data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Bytes_sent'")
+ row['send_bytes'] = data['Value']
+ return row
+
+
+ def getNetList(self, args):
+ from datetime import datetime
+ sid = args['sid']
+ my_instance = self.getInstanceBySid(sid).conn()
+ if my_instance is False:
+ return mw.returnData(False,'无法链接')
+
+ rdata = []
+ row = {}
+ row1 = self.getNetRow(my_instance)
+ # 等待1秒
+ time.sleep(1)
+ row2 = self.getNetRow(my_instance)
+
+ data = my_instance.find("SHOW GLOBAL VARIABLES LIKE 'max_connections'")
+ row['max_conn'] = data['Value']
+
+ data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Threads_connected'")
+ row['conn'] = data['Value']
+
+ current_time = datetime.now()
+ row['current_time'] = current_time.strftime("%Y-%m-%d %H:%M:%S")
+
+ row['select'] = int(row2['select']) - int(row1['select'])
+ row['insert'] = int(row2['insert']) - int(row1['insert'])
+ row['update'] = int(row2['update']) - int(row1['update'])
+ row['delete'] = int(row2['delete']) - int(row1['delete'])
+
+ recv_per_second = int(row2['recv_bytes']) - int(row1['recv_bytes'])
+ send_per_second = int(row2['send_bytes']) - int(row1['send_bytes'])
+
+ # 将每秒接收和发送数据量从字节转换为兆比特
+ row['recv_mbps'] = "{:.2f}".format(recv_per_second * 8 / 1000000) + " MBit/s"
+ row['send_mbps'] = "{:.2f}".format(send_per_second * 8 / 1000000) + " MBit/s"
+
+ rdata.append(row)
+ return mw.returnData(True, 'ok', rdata)
+
+
+ def getTopnList(self, args):
+ sid = args['sid']
+ my_instance = self.getInstanceBySid(sid).conn()
+ if my_instance is False:
+ return mw.returnData(False,'无法链接')
+
+ is_performance_schema = my_instance.find("SELECT @@performance_schema")
+ if is_performance_schema is None:
+ return mw.returnData(False,'异常中断,重试!')
+
+ if is_performance_schema["@@performance_schema"] == 0:
+ msg = "performance_schema参数未开启。\n"
+ msg += "在my.cnf配置文件里添加performance_schema=1,并重启mysqld进程生效。"
+ return mw.returnData(False, msg)
+
+ my_instance.execute("SET @sys.statement_truncate_len=4096")
+ data = my_instance.query("select query,db,last_seen,exec_count,max_latency,avg_latency from sys.statement_analysis order by exec_count desc, last_seen desc limit 20")
+ if data is None:
+ return mw.returnData(False, "查询失败!")
+
+ filter_db = args['filter_db']
+ if filter_db == 'yes':
+ new_data = []
+ for x in data:
+ if x['db'] is not None:
+ new_data.append(x)
+ return mw.returnData(True, 'ok', new_data)
+ return mw.returnData(True, 'ok', data)
+
+ # 查看重复或冗余的索引
+ def getRedundantIndexes(self, args):
+ sid = args['sid']
+ my_instance = self.getInstanceBySid(sid).conn()
+ if my_instance is False:
+ return mw.returnData(False,'无法链接')
+
+ is_performance_schema = my_instance.find("SELECT @@performance_schema")
+ if is_performance_schema["@@performance_schema"] == 0:
+ msg = "performance_schema参数未开启。\n"
+ msg += "在my.cnf配置文件里添加performance_schema=1,并重启mysqld进程生效。"
+ return mw.returnData(False, msg)
+
+ data = my_instance.query("select table_schema,table_name,redundant_index_name,redundant_index_columns,sql_drop_index from sys.schema_redundant_indexes")
+ if data is None:
+ return mw.returnData(False, "查询失败!")
+ # print(data)
+ return mw.returnData(True, 'ok', data)
+
+ def redundantIndexesCmd(self, args):
+ sid = args['sid']
+ my_instance = self.getInstanceBySid(sid).conn()
+ if my_instance is False:
+ return mw.returnData(False,'无法链接')
+
+ is_performance_schema = my_instance.find("SELECT @@performance_schema")
+ if is_performance_schema["@@performance_schema"] == 0:
+ msg = "performance_schema参数未开启。\n"
+ msg += "在my.cnf配置文件里添加performance_schema=1,并重启mysqld进程生效。"
+ return mw.returnData(False, msg)
+
+ data = my_instance.query("select table_schema,table_name,redundant_index_name,redundant_index_columns,sql_drop_index from sys.schema_redundant_indexes")
+ if data is None:
+ return mw.returnData(False, "查询失败!")
+
+ index = int(args['index'])
+
+ cmd = data[index]['sql_drop_index']
+
+ my_instance.execute(cmd)
+ return mw.returnData(True, '执行成功!')
+
+ def getTableInfo(self, args):
+ from decimal import Decimal
+
+ sid = args['sid']
+ my_instance = self.getInstanceBySid(sid).conn()
+ if my_instance is False:
+ return mw.returnData(False,'无法链接')
+
+ my_instance.execute("SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''))")
+ data = my_instance.query(
+ """
+ SELECT t.TABLE_SCHEMA as TABLE_SCHEMA, t.TABLE_NAME as TABLE_NAME, t.ENGINE as ENGINE,
+ IFNULL(t.DATA_LENGTH/1024/1024/1024, 0) as DATA_LENGTH,
+ IFNULL(t.INDEX_LENGTH/1024/1024/1024, 0) as INDEX_LENGTH,
+ IFNULL((DATA_LENGTH+INDEX_LENGTH)/1024/1024/1024, 0) AS TOTAL_LENGTH,
+ c.column_name AS COLUMN_NAME, c.data_type AS DATA_TYPE, c.COLUMN_TYPE AS COLUMN_TYPE,
+ t.AUTO_INCREMENT AS AUTO_INCREMENT, locate('unsigned', c.COLUMN_TYPE) = 0 AS IS_SIGNED
+ FROM information_schema.TABLES t
+ JOIN information_schema.COLUMNS c ON t.TABLE_SCHEMA = c.TABLE_SCHEMA AND t.table_name=c.table_name
+ WHERE t.TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
+ GROUP BY TABLE_NAME
+ ORDER BY TOTAL_LENGTH DESC, AUTO_INCREMENT DESC;
+ """
+ )
+
+ if data is None:
+ return mw.returnData(True, 'ok', [])
+
+ for i in range(len(data)):
+ row = data[i]
+
+ data[i]['DATA_LENGTH'] = round(row['DATA_LENGTH'] or 0, 2)
+ data[i]['INDEX_LENGTH'] = round(row['INDEX_LENGTH'] or 0, 2)
+ data[i]['TOTAL_LENGTH'] = round(row['TOTAL_LENGTH'] or 0, 2)
+
+ AUTO_INCREMENT = row['AUTO_INCREMENT']
+ DATA_TYPE = row['DATA_TYPE']
+ IS_SIGNED = row['IS_SIGNED']
+ RESIDUAL_AUTO_INCREMENT = 0
+ if AUTO_INCREMENT is not None:
+ if DATA_TYPE == 'tinyint':
+ if IS_SIGNED == 0:
+ RESIDUAL_AUTO_INCREMENT = int(255 - AUTO_INCREMENT)
+ if IS_SIGNED == 1:
+ RESIDUAL_AUTO_INCREMENT = int(127 - AUTO_INCREMENT)
+
+ if DATA_TYPE == 'smallint':
+ if IS_SIGNED == 0:
+ RESIDUAL_AUTO_INCREMENT = int(65535 - AUTO_INCREMENT)
+ if IS_SIGNED == 1:
+ RESIDUAL_AUTO_INCREMENT = int(32767 - AUTO_INCREMENT)
+
+ if DATA_TYPE == 'int':
+ if IS_SIGNED == 0:
+ RESIDUAL_AUTO_INCREMENT = int(4294967295 - AUTO_INCREMENT)
+ if IS_SIGNED == 1:
+ RESIDUAL_AUTO_INCREMENT = int(2147483647 - AUTO_INCREMENT)
+
+ if DATA_TYPE == 'mediumint':
+ if IS_SIGNED == 0:
+ RESIDUAL_AUTO_INCREMENT = int(16777215 - AUTO_INCREMENT)
+ if IS_SIGNED == 1:
+ RESIDUAL_AUTO_INCREMENT = int(8388607 - AUTO_INCREMENT)
+
+ if DATA_TYPE == 'bigint':
+ if IS_SIGNED == 0:
+ RESIDUAL_AUTO_INCREMENT = Decimal("18446744073709551615") - Decimal(AUTO_INCREMENT)
+ if IS_SIGNED == 1:
+ RESIDUAL_AUTO_INCREMENT = Decimal("9223372036854775807") - Decimal(AUTO_INCREMENT)
+ else:
+ RESIDUAL_AUTO_INCREMENT = "主键非自增"
+ data[i]['RESIDUAL_AUTO_INCREMENT'] = RESIDUAL_AUTO_INCREMENT
+ return mw.returnData(True, 'ok', data)
+
+ # 查看应用端IP连接数总和
+ def getConnCount(self, args):
+ sid = args['sid']
+ my_instance = self.getInstanceBySid(sid).conn()
+ if my_instance is False:
+ return mw.returnData(False,'无法链接')
+
+ data = my_instance.query(
+ "SELECT user,db,substring_index(HOST,':',1) AS Client_IP,count(1) AS count FROM information_schema.PROCESSLIST "
+ "GROUP BY user,db,substring_index(HOST,':',1) ORDER BY COUNT(1) DESC"
+ )
+
+ for i in range(len(data)):
+ if data[i]['db'] is None:
+ data[i]['db'] = '空'
+
+ if data[i]['Client_IP'] == '':
+ data[i]['Client_IP'] = '空'
+
+ # data2 = my_instance.query("SELECT USER, COUNT(*) as nums FROM information_schema.PROCESSLIST GROUP BY USER ORDER BY COUNT(*) DESC")
+ # print(data)
+ # print(data2)
+ return mw.returnData(True, 'ok', data)
+
+ # 快速找出没有主键的表
+ def getFpkInfo(self, args):
+ sid = args['sid']
+ my_instance = self.getInstanceBySid(sid).conn()
+ if my_instance is False:
+ return mw.returnData(False,'无法链接')
+
+ data = my_instance.query(
+ """
+ SELECT t.table_schema,
+ t.table_name
+ FROM information_schema.tables t
+ LEFT JOIN information_schema.key_column_usage k
+ ON t.table_schema = k.table_schema
+ AND t.table_name = k.table_name
+ AND k.constraint_name = 'PRIMARY'
+ WHERE t.table_schema NOT IN ('mysql', 'information_schema', 'sys', 'performance_schema')
+ AND k.constraint_name IS NULL
+ AND t.table_type = 'BASE TABLE';
+ """
+ )
+ return mw.returnData(True, 'ok', data)
+
+ def getLockSql(self, args):
+ sid = args['sid']
+ my_instance = self.getInstanceBySid(sid).conn()
+ if my_instance is False:
+ return mw.returnData(False,'无法链接')
+
+ data = my_instance.query(
+ """
+ SELECT
+ a.trx_id AS trx_id,
+ a.trx_state AS trx_state,
+ a.trx_started AS trx_started,
+ b.id AS processlist_id,
+ b.info AS info,
+ b.user AS user,
+ b.host AS host,
+ b.db AS db,
+ b.command AS command,
+ b.state AS state,
+ CONCAT('KILL QUERY ', b.id) AS sql_kill_blocking_query
+ FROM
+ information_schema.INNODB_TRX a,
+ information_schema.PROCESSLIST b
+ WHERE
+ a.trx_mysql_thread_id = b.id
+ ORDER BY
+ a.trx_started
+ """
+ )
+ return mw.returnData(True, 'ok', data)
+
+ def killLockPid(self, args):
+ sid = args['sid']
+ my_instance = self.getInstanceBySid(sid).conn()
+ if my_instance is False:
+ return mw.returnData(False,'无法链接')
+
+ pid = args['pid']
+ my_instance.execute('kill %s' % pid)
+ return mw.returnData(True, '执行成功!')
+
+ def killAllLock(self, args):
+ sid = args['sid']
+ my_instance = self.getInstanceBySid(sid).conn()
+ if my_instance is False:
+ return mw.returnData(False,'无法链接')
+
+ data = self.getLockSql(args)
+ if data['status']:
+ pid_data = data['data']
+ for x in pid_data:
+ cmd = 'kill %s' % x['processlist_id']
+ # print(cmd)
+ my_instance.execute(cmd)
+ return mw.returnData(True, '执行成功!')
+
+
+
+ def getDeadlockInfo(self, args):
+ sid = args['sid']
+ my_instance = self.getInstanceBySid(sid).conn()
+ if my_instance is False:
+ return mw.returnData(False,'无法链接')
+
+ data = my_instance.find("SHOW ENGINE INNODB STATUS")
+ if data is not None:
+ innodb_status = data['Status']
+ deadlock_info = re.search(r"LATEST DETECTED DEADLOCK.*?WE ROLL BACK TRANSACTION\s+\(\d+\)", innodb_status, re.DOTALL)
+ if deadlock_info is None:
+ return mw.returnData(True, 'ok', '无锁表')
+ return mw.returnData(True, 'ok', deadlock_info.group(0))
+ return mw.returnData(True, 'ok', '无锁表')
+
+ def getSlaveStatus(self, args):
+ sid = args['sid']
+ my_instance = self.getInstanceBySid(sid).conn()
+ if my_instance is False:
+ return mw.returnData(False,'无法链接')
+
+ slave_info = my_instance.find('SHOW SLAVE STATUS')
+ if slave_info is None:
+ return mw.returnData(True, 'ok', '未开启从库!')
+
+ msg = ''
+ if slave_info['Auto_Position'] != 1:
+ msg += '你没有开启基于GTID全局事务ID复制,请确保CHANGE MASTER TO MASTER_AUTO_POSITION = 1.\n'
+
+
+ if slave_info['Slave_IO_Running'] == 'Yes' and slave_info['Slave_SQL_Running'] == 'Yes':
+ if slave_info['Seconds_Behind_Master'] == 0:
+ msg = "同步正常,无延迟"
+ return mw.returnData(True, 'ok', msg)
+ else:
+ return mw.returnData(True, 'ok', '同步正常,但有延迟,延迟时间为:%s' % slave_info['Seconds_Behind_Master'])
+ else:
+ msg = '主从复制报错,请检查\nSlave_IO_Running状态值是:%s, | Slave_SQL_Running状态值是:%s\nLast_Error错误信息是:%s\nLast_SQL_Error错误信息是:%s\n' \
+ % (slave_info['Slave_IO_Running'], slave_info['Slave_SQL_Running'], slave_info['Last_Error'], slave_info['Last_SQL_Error'])
+ error_dict = my_instance.find('select LAST_ERROR_NUMBER,LAST_ERROR_MESSAGE,LAST_ERROR_TIMESTAMP from performance_schema.replication_applier_status_by_worker ORDER BY LAST_ERROR_TIMESTAMP desc limit 1')
+ msg += '错误号是:%s \n' % error_dict['LAST_ERROR_NUMBER']
+ msg += '错误信息是:%s \n' % error_dict['LAST_ERROR_MESSAGE']
+ msg += '报错时间是:%s \n' % error_dict['LAST_ERROR_TIMESTAMP']
+ msg += 'MySQL Replication Health is NOT OK!'
+ return mw.returnData(True, 'ok', msg)
+
# ---------------------------------- run ----------------------------------
+
+def get_server_list(args):
+ t = nosqlMySQLCtr()
+ return t.getServerList(args)
+
# 获取 mysql 列表
def get_db_list(args):
t = nosqlMySQLCtr()
@@ -307,6 +677,70 @@ def get_stats_list(args):
t = nosqlMySQLCtr()
return t.showStatsList(args)
+# 查询执行次数最频繁的前N条SQL语句
+def get_topn_list(args):
+ t = nosqlMySQLCtr()
+ return t.getTopnList(args)
+
+# MySQL服务器的QPS/TPS/网络带宽指标
+def get_net_list(args):
+ t = nosqlMySQLCtr()
+ return t.getNetList(args)
+
+# 查看重复或冗余的索引
+def get_redundant_indexes(args):
+ t = nosqlMySQLCtr()
+ return t.getRedundantIndexes(args)
+
+# 删除重复或冗余的索引
+def redundant_indexes_cmd(args):
+ t = nosqlMySQLCtr()
+ return t.redundantIndexesCmd(args)
+
+# 统计库里每个表的大小
+def get_table_info(args):
+ t = nosqlMySQLCtr()
+ return t.getTableInfo(args)
+
+# 查看应用端IP连接数总和
+def get_conn_count(args):
+ t = nosqlMySQLCtr()
+ return t.getConnCount(args)
+
+# 快速找出没有主键的表
+def get_fpk_info(args):
+ t = nosqlMySQLCtr()
+ return t.getFpkInfo(args)
+
+# 查看当前锁阻塞的SQL
+def get_lock_sql(args):
+ t = nosqlMySQLCtr()
+ return t.getLockSql(args)
+
+# KILL阻塞SQL
+def kill_lock_pid(args):
+ t = nosqlMySQLCtr()
+ return t.killLockPid(args)
+
+# KILL阻塞SQL
+def kill_all_lock(args):
+ t = nosqlMySQLCtr()
+ return t.killAllLock(args)
+
+# 查看死锁信息
+def get_deadlock_info(args):
+ t = nosqlMySQLCtr()
+ return t.getDeadlockInfo(args)
+
+# 查看主从复制信息
+def get_slave_status(args):
+ t = nosqlMySQLCtr()
+ return t.getSlaveStatus(args)
+
+
+
+
+
# 测试
def test(args):
diff --git a/plugins/data_query/static/html/index.html b/plugins/data_query/static/html/index.html
index c6bcd3ab0..b07cef4e2 100644
--- a/plugins/data_query/static/html/index.html
+++ b/plugins/data_query/static/html/index.html
@@ -51,9 +51,11 @@
-
+
+
+
';
+ t += ' ';
+ tbody += t;
+ }
+ $('#topn_list tbody').html(tbody);
+ } else {
+ layer.msg(data.msg,{icon:2});
+ }
+ });
+ }
+
+ var sql_timer = null;
+ layer.open({
+ type: 1,
+ title: "查询执行次数最频繁的前N条SQL语句",
+ area: ['1200px', '500px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: ''+items[i].query+' ';
+ t += ''+items[i].db+' ';
+ t += ''+items[i].last_seen+' ';
+ t += ''+items[i].exec_count+' ';
+ t += ''+items[i].max_latency+' ';
+ t += ''+items[i].avg_latency+' ';
+ t += '
\
+ \
+ \
+
\
+ SQL \
+ 数据名 \
+ 最近时间 \
+ 总次数 \
+ 最大时间 \
+ 平均时间 \
+ \
+ \
+ ';
+ t += ' ';
+ tbody += t;
+ }
+ $('#net_list tbody').html(tbody);
+ } else {
+ layer.msg(data.msg,{icon:2});
+ }
+ });
+ }
+
+ var sql_timer = null;
+ layer.open({
+ type: 1,
+ title: "MySQL服务器的QPS/TPS/网络带宽指标",
+ area: ['750px', '220px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: ''+items[i]['current_time']+' ';
+ t += ''+items[i]['select']+' ';
+ t += ''+items[i]['insert']+' ';
+ t += ''+items[i]['update']+' ';
+ t += ''+items[i]['delete']+' ';
+ t += ''+items[i]['conn']+' ';
+ t += ''+items[i]['max_conn']+' ';
+ t += ''+items[i]['recv_mbps']+' ';
+ t += ''+items[i]['send_mbps']+' ';
+ t += '
\
+ \
+ \
+
\
+ 时间 \
+ Select \
+ Insert \
+ Update \
+ Delete \
+ Conn \
+ Max_conn \
+ Recv \
+ Send \
+ \
+ \
+ ';
+ t += ' ';
+ tbody += t;
+ }
+ $('#redundant_indexes tbody').html(tbody);
+ $('#redundant_indexes tbody .exec').click(function(){
+ var index = $(this).attr('index');
+ myPostCB('redundant_indexes_cmd', {'sid':sid, 'index':index}, function(rdata){
+ var data = rdata.data;
+ showMsg(data.msg,function(){
+ if (data.status){
+ renderSQL();
+ }
+ },{icon: data.status ? 1 : 2}, 2000);
+ });
+ });
+ } else {
+ layer.msg(data.msg,{icon:2});
+ }
+ });
+ }
+
+ layer.open({
+ type: 1,
+ title: "查看重复或冗余的索引",
+ area: ['1100px', '400px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: ''+items[i]['table_schema']+' ';
+ t += ''+items[i]['table_name']+' ';
+ t += ''+items[i]['redundant_index_name']+' ';
+ t += ''+items[i]['redundant_index_columns']+' ';
+ t += ''+items[i]['sql_drop_index']+' ';
+ t += '执行 ';
+ t += '\
+ \
+
\
+ 数据库名 \
+ 表名 \
+ 冗余索引名 \
+ 冗余索引列名 \
+ 删除冗余索引SQL \
+ 操作 \
+ \
+ \
+ ';
+ t += ' ';
+ tbody += t;
+ }
+ $('#mysql_data_id tbody').html(tbody);
+ } else {
+ layer.msg(data.msg,{icon:2});
+ }
+ });
+ }
+
+ layer.open({
+ type: 1,
+ title: "统计库里每个表的大小",
+ area: ['1200px', '400px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: ''+items[i]['TABLE_SCHEMA']+' ';
+ t += ''+items[i]['TABLE_NAME']+' ';
+ t += ''+items[i]['ENGINE']+' ';
+ t += ''+items[i]['DATA_LENGTH']+' ';
+ t += ''+items[i]['INDEX_LENGTH']+' ';
+ t += ''+items[i]['TOTAL_LENGTH']+' ';
+ t += ''+items[i]['COLUMN_NAME']+' ';
+ t += ''+items[i]['COLUMN_TYPE']+' ';
+ t += ''+items[i]['AUTO_INCREMENT']+' ';
+ t += ''+items[i]['RESIDUAL_AUTO_INCREMENT']+' ';
+ t += '\
+ \
+
\
+ 库名 \
+ 表名 \
+ 储存引擎 \
+ 数据大小(GB) \
+ 索引大小(GB) \
+ 总计(GB) \
+ 主键自增字段 \
+ 主键字段属性 \
+ 主键自增当前 \
+ 主键自增剩余 \
+ \
+ \
+ ';
+ t += ' ';
+ tbody += t;
+ }
+ $('#app_ip_list tbody').html(tbody);
+ } else {
+ layer.msg(data.msg,{icon:2});
+ }
+ });
+ }
+
+ var sql_timer = null;
+ layer.open({
+ type: 1,
+ title: "查看应用端IP连接数总和",
+ area: ['700px', '420px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: ''+items[i]['user']+' ';
+ t += ''+items[i]['db']+' ';
+ t += ''+items[i]['Client_IP']+' ';
+ t += ''+items[i]['count']+' ';
+ t += '
\
+ \
+ \
+
\
+ 连接用户 \
+ 数据库名 \
+ 应用端IP \
+ 数量 \
+ \
+ \
+ ';
+ t += ' ';
+ tbody += t;
+ }
+ $('#mysql_data_id tbody').html(tbody);
+ } else {
+ layer.msg(data.msg,{icon:2});
+ }
+ });
+ }
+
+ layer.open({
+ type: 1,
+ title: "快速找出没有主键的表",
+ area: ['800px', '400px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: ''+items[i]['table_schema']+' ';
+ t += ''+items[i]['table_name']+' ';
+ t += '\
+ \
+
\
+ 库名 \
+ 表名 \
+ \
+ \
+ ';
+ t += ' ';
+ tbody += t;
+ }
+ $('#mysql_data_id tbody').html(tbody);
+
+ $('#mysql_data_id tbody .exec').click(function(){
+ var index = $(this).attr('index');
+ var pid = items[index]['processlist_id'];
+ myPostCB('kill_lock_pid', {'sid':sid, 'pid':pid}, function(rdata){
+ var data = rdata.data;
+ showMsg(data.msg,function(){
+ if (data.status){
+ renderSQL();
+ }
+ },{icon: data.status ? 1 : 2}, 2000);
+ });
+ });
+ } else {
+ layer.msg(data.msg,{icon:2});
+ }
+ });
+ }
+
+ layer.open({
+ type: 1,
+ title: "查看当前锁阻塞的SQL",
+ area: ['1000px', '400px'],
+ closeBtn: 1,
+ shadeClose: false,
+ content: ''+items[i]['trx_id']+' ';
+ t += ''+items[i]['trx_state']+' ';
+ t += ''+items[i]['trx_started']+' ';
+ t += ''+items[i]['processlist_id']+' ';
+ t += ''+items[i]['info']+' ';
+ t += ''+items[i]['user']+' ';
+ t += ''+items[i]['host']+' ';
+ t += ''+items[i]['db']+' ';
+ t += ''+items[i]['command']+' ';
+ t += ''+items[i]['state']+' ';
+ t += ''+items[i]['sql_kill_blocking_query']+' ';
+ t += '执行 ';
+ t += '
\
+ \
+ \
+
\
+ 事务ID \
+ 事务状态 \
+ 执行时间 \
+ 线程ID \
+ Info \
+ user \
+ host \
+ db \
+ command \
+ state \
+ kill \
+ 操作 \
+ \
+ \
+ '+data.data+'
\
+
rm -rf {}".format(version, getServerDir())
if __name__ == "__main__":
diff --git a/plugins/mysql-apt/index_mysql_apt.py b/plugins/mysql-apt/index_mysql_apt.py
index a61c2a015..d252c1cea 100644
--- a/plugins/mysql-apt/index_mysql_apt.py
+++ b/plugins/mysql-apt/index_mysql_apt.py
@@ -8,9 +8,12 @@ import subprocess
import re
import json
-sys.path.append(os.getcwd() + "/class/core")
-import mw
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+import core.mw as mw
if mw.isAppleSystem():
cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\''
@@ -48,7 +51,7 @@ def getConf():
def getDataDir():
file = getConf()
content = mw.readFile(file)
- rep = 'datadir\s*=\s*(.*)'
+ rep = r'datadir\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
@@ -56,7 +59,7 @@ def getDataDir():
def getRelayLogName():
file = getConf()
content = mw.readFile(file)
- rep = 'relay-log\s*=\s*(.*)'
+ rep = r'relay-log\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
@@ -64,7 +67,7 @@ def getRelayLogName():
def getLogBinName():
file = getConf()
content = mw.readFile(file)
- rep = 'log-bin\s*=\s*(.*)'
+ rep = r'log-bin\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
diff --git a/plugins/mysql-apt/install.sh b/plugins/mysql-apt/install.sh
index 99ec491e5..db63dcd32 100755
--- a/plugins/mysql-apt/install.sh
+++ b/plugins/mysql-apt/install.sh
@@ -15,7 +15,6 @@ serverPath=$(dirname "$rootPath")
# cd /www/server/mdserver-web && python3 /www/server/mdserver-web/plugins/mysql-apt/index.py fix_db_access
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py do_full_sync {"db":"xxx","sign":"","begin":1}
-install_tmp=${rootPath}/tmp/mw_install.pl
action=$1
diff --git a/plugins/mysql-apt/scripts/backup.py b/plugins/mysql-apt/scripts/backup.py
index 992219655..a058e5d23 100755
--- a/plugins/mysql-apt/scripts/backup.py
+++ b/plugins/mysql-apt/scripts/backup.py
@@ -5,22 +5,19 @@
import sys
import os
+import time
+import re
if sys.platform != 'darwin':
os.chdir('/www/server/mdserver-web')
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
-chdir = os.getcwd()
-sys.path.append(chdir + '/class/core')
-
-# reload(sys)
-# sys.setdefaultencoding('utf-8')
-
-
-import mw
-import db
-import time
-import re
+import core.mw as mw
+import core.db as db
'''
DEBUG:
@@ -44,7 +41,7 @@ class backupTools:
"----------------------------------------------------------------------------")
return
- backup_path = mw.getRootDir() + '/backup/database/mysql-apt'
+ backup_path = mw.getFatherDir() + '/backup/database/mysql-apt'
if not os.path.exists(backup_path):
mw.execShell("mkdir -p " + backup_path)
@@ -56,7 +53,7 @@ class backupTools:
my_conf_path = db_path + '/etc/my.cnf'
mycnf = mw.readFile(my_conf_path)
- rep = "\[mysqldump\]\nuser=root"
+ rep = r"\[mysqldump\]\nuser=root"
sea = "[mysqldump]\n"
subStr = sea + "user=root\npassword=" + mysql_root + "\n"
mycnf = mycnf.replace(sea, subStr)
@@ -82,11 +79,9 @@ class backupTools:
endDate = time.strftime('%Y/%m/%d %X', time.localtime())
outTime = time.time() - startTime
- pid = mw.M('databases').dbPos(db_path, db_name).where(
- 'name=?', (name,)).getField('id')
+ pid = mw.M('databases').dbPos(db_path, db_name).where('name=?', (name,)).getField('id')
- mw.M('backup').add('type,name,pid,filename,addtime,size', (1, os.path.basename(
- filename), pid, filename, endDate, os.path.getsize(filename)))
+ mw.M('backup').add('type,name,pid,filename,add_time,size', (1, os.path.basename(filename), pid, filename, endDate, os.path.getsize(filename)))
log = "数据库[" + name + "]备份成功,用时[" + str(round(outTime, 2)) + "]秒"
mw.writeLog('计划任务', log)
print("★[" + endDate + "] " + log)
@@ -94,8 +89,7 @@ class backupTools:
print("|---文件名:" + filename)
# 清理多余备份
- backups = mw.M('backup').where(
- 'type=? and pid=?', ('1', pid)).field('id,filename').select()
+ backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select()
num = len(backups) - int(count)
if num > 0:
@@ -110,8 +104,7 @@ class backupTools:
def backupDatabaseAll(self, save):
db_path = mw.getServerDir() + '/mysql-apt'
db_name = 'mysql'
- databases = mw.M('databases').dbPos(
- db_path, db_name).field('name').select()
+ databases = mw.M('databases').dbPos(db_path, db_name).field('name').select()
for database in databases:
self.backupDatabase(database['name'], save)
diff --git a/plugins/mysql-apt/versions/5.7/install.sh b/plugins/mysql-apt/versions/5.7/install.sh
index 36af92dc8..46ec670ed 100755
--- a/plugins/mysql-apt/versions/5.7/install.sh
+++ b/plugins/mysql-apt/versions/5.7/install.sh
@@ -17,7 +17,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-install_tmp=${rootPath}/tmp/mw_install.pl
myDir=${serverPath}/source/mysql-apt
bash ${rootPath}/scripts/getos.sh
@@ -46,7 +45,7 @@ else
fi
if [ "$ARCH" != "amd64" ];then
- echo "暂时不支持该${ARCH}" > $install_tmp
+ echo "暂时不支持该${ARCH}"
exit 0
fi
@@ -95,7 +94,7 @@ rm -rf $myDir
Install_mysql()
{
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
isApt=`which apt`
if [ "$isApt" != "" ];then
@@ -105,9 +104,9 @@ Install_mysql()
if [ "$?" == "0" ];then
mkdir -p $serverPath/mysql-apt
echo '5.7' > $serverPath/mysql-apt/version.pl
- echo '安装完成' > $install_tmp
+ echo '安装完成'
else
- echo "暂时不支持该系统" > $install_tmp
+ echo "暂时不支持该系统"
fi
}
@@ -120,7 +119,7 @@ Uninstall_mysql()
fi
rm -rf $serverPath/mysql-apt
- echo '卸载完成' > $install_tmp
+ echo '卸载完成'
}
action=$1
diff --git a/plugins/mysql-apt/versions/8.0/install.sh b/plugins/mysql-apt/versions/8.0/install.sh
index fe9d64f03..582e3aab5 100755
--- a/plugins/mysql-apt/versions/8.0/install.sh
+++ b/plugins/mysql-apt/versions/8.0/install.sh
@@ -13,7 +13,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-install_tmp=${rootPath}/tmp/mw_install.pl
myDir=${serverPath}/source/mysql-apt
bash ${rootPath}/scripts/getos.sh
@@ -24,7 +23,7 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -
# 暂时debian12没有标准版,先用11使用
# if [ "$OSNAME" == 'debian' ] && [ "$VERSION_ID" == '12' ] ;then
-# echo "暂时不支持该${OSNAME}${VERSION_ID}" > $install_tmp
+# echo "暂时不支持该${OSNAME}${VERSION_ID}"
# exit 1
# fi
@@ -40,7 +39,7 @@ else
fi
if [ "$ARCH" != "amd64" ];then
- echo "暂时不支持该${ARCH}" > $install_tmp
+ echo "暂时不支持该${ARCH}"
exit 1
fi
@@ -100,7 +99,7 @@ rm -rf $myDir
Install_mysql()
{
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
isApt=`which apt`
if [ "$isApt" != "" ];then
@@ -110,10 +109,10 @@ Install_mysql()
if [ "$?" == "0" ];then
mkdir -p $serverPath/mysql-apt
echo '8.0' > $serverPath/mysql-apt/version.pl
- echo '安装完成' > $install_tmp
+ echo '安装完成'
else
echo '8.0' > $serverPath/mysql-apt/version.pl
- echo "暂时不支持该系统" > $install_tmp
+ echo "暂时不支持该系统"
fi
}
@@ -126,7 +125,7 @@ Uninstall_mysql()
fi
rm -rf $serverPath/mysql-apt
- echo '卸载完成' > $install_tmp
+ echo '卸载完成'
}
action=$1
diff --git a/plugins/mysql-apt/versions/8.2/install.sh b/plugins/mysql-apt/versions/8.2/install.sh
index 3866488f4..81cade826 100755
--- a/plugins/mysql-apt/versions/8.2/install.sh
+++ b/plugins/mysql-apt/versions/8.2/install.sh
@@ -14,7 +14,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-install_tmp=${rootPath}/tmp/mw_install.pl
myDir=${serverPath}/source/mysql-apt
bash ${rootPath}/scripts/getos.sh
@@ -26,7 +25,7 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -
# 暂时debian12没有标准版,先用11使用
# if [ "$OSNAME" == 'debian' ] && [ "$VERSION_ID" == '12' ] ;then
-# echo "暂时不支持该${OSNAME}${VERSION_ID}" > $install_tmp
+# echo "暂时不支持该${OSNAME}${VERSION_ID}"
# exit 1
# fi
@@ -42,7 +41,7 @@ else
fi
if [ "$ARCH" != "amd64" ];then
- echo "暂时不支持该${ARCH}" > $install_tmp
+ echo "暂时不支持该${ARCH}"
exit 1
fi
@@ -101,7 +100,7 @@ rm -rf $myDir
Install_mysql()
{
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
isApt=`which apt`
if [ "$isApt" != "" ];then
@@ -111,10 +110,10 @@ Install_mysql()
if [ "$?" == "0" ];then
mkdir -p $serverPath/mysql-apt
echo '8.2' > $serverPath/mysql-apt/version.pl
- echo '安装完成' > $install_tmp
+ echo '安装完成'
else
echo '8.2' > $serverPath/mysql-apt/version.pl
- echo "暂时不支持该系统" > $install_tmp
+ echo "暂时不支持该系统"
fi
}
@@ -127,7 +126,7 @@ Uninstall_mysql()
fi
rm -rf $serverPath/mysql-apt
- echo '卸载完成' > $install_tmp
+ echo '卸载完成'
}
action=$1
diff --git a/plugins/mysql-apt/versions/8.3/install.sh b/plugins/mysql-apt/versions/8.3/install.sh
index e5b1e449f..316ae77ed 100755
--- a/plugins/mysql-apt/versions/8.3/install.sh
+++ b/plugins/mysql-apt/versions/8.3/install.sh
@@ -13,7 +13,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-install_tmp=${rootPath}/tmp/mw_install.pl
myDir=${serverPath}/source/mysql-apt
bash ${rootPath}/scripts/getos.sh
@@ -24,7 +23,7 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -
# 暂时debian12没有标准版,先用11使用
# if [ "$OSNAME" == 'debian' ] && [ "$VERSION_ID" == '12' ] ;then
-# echo "暂时不支持该${OSNAME}${VERSION_ID}" > $install_tmp
+# echo "暂时不支持该${OSNAME}${VERSION_ID}"
# exit 1
# fi
@@ -40,7 +39,7 @@ else
fi
if [ "$ARCH" != "amd64" ];then
- echo "暂时不支持该${ARCH}" > $install_tmp
+ echo "暂时不支持该${ARCH}"
exit 1
fi
@@ -99,7 +98,7 @@ rm -rf $myDir
Install_mysql()
{
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
isApt=`which apt`
if [ "$isApt" != "" ];then
@@ -109,10 +108,10 @@ Install_mysql()
if [ "$?" == "0" ];then
mkdir -p $serverPath/mysql-apt
echo '8.3' > $serverPath/mysql-apt/version.pl
- echo '安装完成' > $install_tmp
+ echo '安装完成'
else
echo '8.3' > $serverPath/mysql-apt/version.pl
- echo "暂时不支持该系统" > $install_tmp
+ echo "暂时不支持该系统"
fi
}
@@ -125,7 +124,7 @@ Uninstall_mysql()
fi
rm -rf $serverPath/mysql-apt
- echo '卸载完成' > $install_tmp
+ echo '卸载完成'
}
action=$1
diff --git a/plugins/mysql-apt/versions/8.4/install.sh b/plugins/mysql-apt/versions/8.4/install.sh
index a3647de16..04124e5b9 100755
--- a/plugins/mysql-apt/versions/8.4/install.sh
+++ b/plugins/mysql-apt/versions/8.4/install.sh
@@ -13,7 +13,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-install_tmp=${rootPath}/tmp/mw_install.pl
myDir=${serverPath}/source/mysql-apt
bash ${rootPath}/scripts/getos.sh
@@ -24,7 +23,7 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -
# 暂时debian12没有标准版,先用11使用
# if [ "$OSNAME" == 'debian' ] && [ "$VERSION_ID" == '12' ] ;then
-# echo "暂时不支持该${OSNAME}${VERSION_ID}" > $install_tmp
+# echo "暂时不支持该${OSNAME}${VERSION_ID}"
# exit 1
# fi
@@ -40,7 +39,7 @@ else
fi
if [ "$ARCH" != "amd64" ];then
- echo "暂时不支持该${ARCH}" > $install_tmp
+ echo "暂时不支持该${ARCH}"
exit 1
fi
@@ -102,7 +101,7 @@ rm -rf $myDir
Install_mysql()
{
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
isApt=`which apt`
if [ "$isApt" != "" ];then
@@ -128,7 +127,7 @@ Uninstall_mysql()
fi
rm -rf $serverPath/mysql-apt
- echo '卸载完成' > $install_tmp
+ echo '卸载完成'
}
action=$1
diff --git a/plugins/mysql-apt/versions/9.0/install.sh b/plugins/mysql-apt/versions/9.0/install.sh
index 9c6073b0c..e3cf77bae 100755
--- a/plugins/mysql-apt/versions/9.0/install.sh
+++ b/plugins/mysql-apt/versions/9.0/install.sh
@@ -13,7 +13,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-install_tmp=${rootPath}/tmp/mw_install.pl
myDir=${serverPath}/source/mysql-apt
bash ${rootPath}/scripts/getos.sh
@@ -24,7 +23,7 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -
# 暂时debian12没有标准版,先用11使用
# if [ "$OSNAME" == 'debian' ] && [ "$VERSION_ID" == '12' ] ;then
-# echo "暂时不支持该${OSNAME}${VERSION_ID}" > $install_tmp
+# echo "暂时不支持该${OSNAME}${VERSION_ID}"
# exit 1
# fi
@@ -40,7 +39,7 @@ else
fi
if [ "$ARCH" != "amd64" ];then
- echo "暂时不支持该${ARCH}" > $install_tmp
+ echo "暂时不支持该${ARCH}"
exit 1
fi
@@ -102,7 +101,7 @@ rm -rf $myDir
Install_mysql()
{
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
isApt=`which apt`
if [ "$isApt" != "" ];then
diff --git a/plugins/mysql-yum/index.py b/plugins/mysql-yum/index.py
index 2ce17b7b3..4581ebd86 100755
--- a/plugins/mysql-yum/index.py
+++ b/plugins/mysql-yum/index.py
@@ -9,18 +9,18 @@ import re
import json
-# reload(sys)
-# sys.setdefaultencoding('utf-8')
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
-sys.path.append(os.getcwd() + "/class/core")
-import mw
+import core.mw as mw
-
-if mw.isAppleSystem():
- cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\''
- info = mw.execShell(cmd)
- p = "/usr/local/lib/" + info[0].strip() + "/site-packages"
- sys.path.append(p)
+# if mw.isAppleSystem():
+# cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\''
+# info = mw.execShell(cmd)
+# p = "/usr/local/lib/" + info[0].strip() + "/site-packages"
+# sys.path.append(p)
app_debug = False
@@ -85,21 +85,21 @@ def getConf():
def getDataDir():
file = getConf()
content = mw.readFile(file)
- rep = 'datadir\s*=\s*(.*)'
+ rep = r'datadir\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
def getLogBinName():
file = getConf()
content = mw.readFile(file)
- rep = 'log-bin\s*=\s*(.*)'
+ rep = r'log-bin\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
def getPidFile():
file = getConf()
content = mw.readFile(file)
- rep = 'pid-file\s*=\s*(.*)'
+ rep = r'pid-file\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
@@ -107,7 +107,7 @@ def getPidFile():
def getDbPort():
file = getConf()
content = mw.readFile(file)
- rep = 'port\s*=\s*(.*)'
+ rep = r'port\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
@@ -115,7 +115,7 @@ def getDbPort():
def getDbServerId():
file = getConf()
content = mw.readFile(file)
- rep = 'server-id\s*=\s*(.*)'
+ rep = r'server-id\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
@@ -123,7 +123,7 @@ def getDbServerId():
def getSocketFile():
file = getConf()
content = mw.readFile(file)
- rep = 'socket\s*=\s*(.*)'
+ rep = r'socket\s*=\s*(.*)'
tmp = re.search(rep, content)
socket = tmp.groups()[0].strip()
@@ -136,14 +136,14 @@ def getSocketFile():
def getErrorLogsFile():
file = getConf()
content = mw.readFile(file)
- rep = 'log-error\s*=\s*(.*)'
+ rep = r'log-error\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
def getAuthPolicy():
file = getConf()
content = mw.readFile(file)
- rep = 'authentication_policy\s*=\s*(.*)'
+ rep = r'authentication_policy\s*=\s*(.*)'
tmp = re.search(rep, content)
if tmp:
return tmp.groups()[0].strip()
@@ -152,7 +152,7 @@ def getAuthPolicy():
def contentReplace(content):
service_path = mw.getServerDir()
- content = content.replace('{$ROOT_PATH}', mw.getRootDir())
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$SERVER_APP_PATH}',service_path + '/' + getPluginName())
@@ -385,7 +385,7 @@ def getErrorLog():
def getShowLogFile():
file = getConf()
content = mw.readFile(file)
- rep = 'slow-query-log-file\s*=\s*(.*)'
+ rep = r'slow-query-log-file\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
@@ -435,7 +435,7 @@ def initMysql8Data():
return True
def getLib64Pos(name=''):
- cmd = "ls /usr/lib64 | grep "+name+".so | cut -d \ -f 1 | awk 'END {print}'"
+ cmd = "ls /usr/lib64 | grep "+name+".so | cut -d \\ -f 1 | awk 'END {print}'"
d = mw.execShell(cmd)
return '/usr/lib64/'+d[0]
@@ -604,7 +604,7 @@ def initdUinstall():
def getMyDbPos():
file = getConf()
content = mw.readFile(file)
- rep = 'datadir\s*=\s*(.*)'
+ rep = r'datadir\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
@@ -655,7 +655,7 @@ def setMyDbPos(version=''):
def getMyPort():
file = getConf()
content = mw.readFile(file)
- rep = 'port\s*=\s*(.*)'
+ rep = r'port\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
@@ -669,7 +669,7 @@ def setMyPort():
port = args['port']
file = getConf()
content = mw.readFile(file)
- rep = "port\s*=\s*([0-9]+)\s*\n"
+ rep = r"port\s*=\s*([0-9]+)\s*\n"
content = re.sub(rep, 'port = ' + port + '\n', content)
mw.writeFile(file, content)
restart()
@@ -757,7 +757,7 @@ def setDbStatus(version):
s = 'K'
if g in emptys:
s = ''
- rep = '\s*' + g + '\s*=\s*\d+(M|K|k|m|G)?\n'
+ rep = r'\s*' + g + r'\s*=\s*\d+(M|K|k|m|G)?\n'
c = g + ' = ' + args[g] + s + '\n'
if content.find(g) != -1:
content = re.sub(rep, '\n' + c, content, 1)
@@ -930,7 +930,7 @@ def importDbExternalProgressBar():
file = args['file']
name = args['name']
- import_dir = mw.getRootDir() + '/backup/import/'
+ import_dir = mw.getFatherDir() + '/backup/import/'
file_path = import_dir + file
if not os.path.exists(file_path):
@@ -1349,7 +1349,7 @@ def addDb():
dataAccess = args['dataAccess'].strip()
ps = args['ps'].strip()
- reg = "^[\w-]+$"
+ reg = r"^[\w-]+$"
if not re.match(reg, args['name']):
return mw.returnJson(False, '数据库名称不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'performance_schema','information_schema']
@@ -1512,13 +1512,10 @@ def resetDbRootPwd(version):
auth_policy = getAuthPolicy()
reset_pwd = 'flush privileges;'
- reset_pwd = reset_pwd + \
- "UPDATE mysql.user SET authentication_string='' WHERE user='root';"
+ reset_pwd = reset_pwd + "UPDATE mysql.user SET authentication_string='' WHERE user='root';"
reset_pwd = reset_pwd + "flush privileges;"
- reset_pwd = reset_pwd + \
- "alter user 'root'@'localhost' IDENTIFIED by '" + pwd + "';"
- reset_pwd = reset_pwd + \
- "alter user 'root'@'localhost' IDENTIFIED WITH "+auth_policy+" by '" + pwd + "';"
+ reset_pwd = reset_pwd + "alter user 'root'@'localhost' IDENTIFIED by '" + pwd + "';"
+ reset_pwd = reset_pwd + "alter user 'root'@'localhost' IDENTIFIED WITH "+auth_policy+" by '" + pwd + "';"
reset_pwd = reset_pwd + "flush privileges;"
tmp_file = "/tmp/mysql_init_tmp.log"
@@ -2113,7 +2110,7 @@ def addMasterRepSlaveUser(version=''):
username = args['username'].strip()
password = args['password'].strip()
- reg = "^[\w-]+$"
+ reg = r"^[\w-]+$"
if not re.match(reg, username):
return mw.returnJson(False, '用户名不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'performance_schema','information_schema']
@@ -2554,7 +2551,7 @@ def trySlaveSyncBugfix(version=''):
def getSlaveSyncCmd(version=''):
- root = mw.getRunDir()
+ root = mw.getPanelDir()
cmd = 'cd ' + root + ' && python3 ' + root + \
'/plugins/mysql/index.py do_full_sync {"db":"all"}'
return mw.returnJson(True, 'ok', cmd)
@@ -3436,7 +3433,7 @@ def fullSync(version=''):
status_file = asyncTmpfile()
if args['begin'] == '1':
- cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + getPluginDir() + \
+ cmd = 'cd ' + mw.getPanelDir() + ' && python3 ' + getPluginDir() + \
'/index.py do_full_sync {"db":"' + \
args['db'] + '","sign":"' + sign + '"} &'
# print(cmd)
@@ -3490,8 +3487,8 @@ def uninstallPreInspection(version):
if mw.isDebugMode():
return 'ok'
- import plugins_api
- plugins_api.plugins_api().removeIndex(getPluginName(), version)
+ from utils.plugin import plugin as MwPlugin
+ MwPlugin.instance().removeIndex(getPluginName(), version)
return "请手动删除MySQL[{}]
rm -rf {}".format(version, getServerDir())
diff --git a/plugins/mysql-yum/index_mysql_yum.py b/plugins/mysql-yum/index_mysql_yum.py
index 1f7cc3a71..6c10d29f0 100644
--- a/plugins/mysql-yum/index_mysql_yum.py
+++ b/plugins/mysql-yum/index_mysql_yum.py
@@ -8,16 +8,12 @@ import subprocess
import re
import json
-sys.path.append(os.getcwd() + "/class/core")
-import mw
-
-
-if mw.isAppleSystem():
- cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\''
- info = mw.execShell(cmd)
- p = "/usr/local/lib/" + info[0].strip() + "/site-packages"
- sys.path.append(p)
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+import core.mw as mw
app_debug = False
if mw.isAppleSystem():
@@ -48,7 +44,7 @@ def getConf():
def getDataDir():
file = getConf()
content = mw.readFile(file)
- rep = 'datadir\s*=\s*(.*)'
+ rep = r'datadir\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
@@ -56,7 +52,7 @@ def getDataDir():
def getRelayLogName():
file = getConf()
content = mw.readFile(file)
- rep = 'relay-log\s*=\s*(.*)'
+ rep = r'relay-log\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
@@ -64,7 +60,7 @@ def getRelayLogName():
def getLogBinName():
file = getConf()
content = mw.readFile(file)
- rep = 'log-bin\s*=\s*(.*)'
+ rep = r'log-bin\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
diff --git a/plugins/mysql-yum/install.sh b/plugins/mysql-yum/install.sh
index 9a1edcaaf..3fb1a908f 100755
--- a/plugins/mysql-yum/install.sh
+++ b/plugins/mysql-yum/install.sh
@@ -13,8 +13,6 @@ serverPath=$(dirname "$rootPath")
# cd /www/server/mdserver-web && python3 /www/server/mdserver-web/plugins/mysql-yum/index.py start 8.0
# cd /www/server/mdserver-web && python3 /www/server/mdserver-web/plugins/mysql-yum/index.py get_master_status 8.4
-install_tmp=${rootPath}/tmp/mw_install.pl
-
action=$1
type=$2
@@ -30,12 +28,12 @@ fi
if [ "${2}" == "" ];then
- echo '缺少安装脚本...' > $install_tmp
+ echo '缺少安装脚本...'
exit 0
fi
if [ ! -d $curPath/versions/$2 ];then
- echo '缺少安装脚本2...' > $install_tmp
+ echo '缺少安装脚本2...'
exit 0
fi
diff --git a/plugins/mysql-yum/scripts/backup.py b/plugins/mysql-yum/scripts/backup.py
index 1c4dd3cc0..d18caf8e6 100755
--- a/plugins/mysql-yum/scripts/backup.py
+++ b/plugins/mysql-yum/scripts/backup.py
@@ -5,22 +5,20 @@
import sys
import os
+import time
+import re
if sys.platform != 'darwin':
os.chdir('/www/server/mdserver-web')
-chdir = os.getcwd()
-sys.path.append(chdir + '/class/core')
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
-# reload(sys)
-# sys.setdefaultencoding('utf-8')
-
-
-import mw
-import db
-import time
-import re
+import core.mw as mw
+import core.db as db
'''
DEBUG:
@@ -44,7 +42,7 @@ class backupTools:
"----------------------------------------------------------------------------")
return
- backup_path = mw.getRootDir() + '/backup/database/mysql-yum'
+ backup_path = mw.getFatherDir() + '/backup/database/mysql-yum'
if not os.path.exists(backup_path):
mw.execShell("mkdir -p " + backup_path)
@@ -58,7 +56,7 @@ class backupTools:
mw.backFile(my_cnf)
content = mw.readFile(my_cnf)
- rep = "\[mysqldump\]\nuser=root"
+ rep = r"\[mysqldump\]\nuser=root"
sea = "[mysqldump]\n"
subStr = sea + "user=root\npassword=" + mysql_root + "\n"
content = content.replace(sea, subStr)
@@ -80,11 +78,9 @@ class backupTools:
endDate = time.strftime('%Y/%m/%d %X', time.localtime())
outTime = time.time() - startTime
- pid = mw.M('databases').dbPos(db_path, db_name).where(
- 'name=?', (name,)).getField('id')
+ pid = mw.M('databases').dbPos(db_path, db_name).where('name=?', (name,)).getField('id')
- mw.M('backup').add('type,name,pid,filename,addtime,size', (1, os.path.basename(
- filename), pid, filename, endDate, os.path.getsize(filename)))
+ mw.M('backup').add('type,name,pid,filename,add_time,size', (1, os.path.basename(filename), pid, filename, endDate, os.path.getsize(filename)))
log = "数据库[" + name + "]备份成功,用时[" + str(round(outTime, 2)) + "]秒"
mw.writeLog('计划任务', log)
print("★[" + endDate + "] " + log)
@@ -92,9 +88,7 @@ class backupTools:
print("|---文件名:" + filename)
# 清理多余备份
- backups = mw.M('backup').where(
- 'type=? and pid=?', ('1', pid)).field('id,filename').select()
-
+ backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select()
num = len(backups) - int(count)
if num > 0:
for backup in backups:
diff --git a/plugins/mysql-yum/versions/5.7/install.sh b/plugins/mysql-yum/versions/5.7/install.sh
index 734b30d65..e94ac9b89 100755
--- a/plugins/mysql-yum/versions/5.7/install.sh
+++ b/plugins/mysql-yum/versions/5.7/install.sh
@@ -13,7 +13,6 @@ serverPath=$(dirname "$rootPath")
sysName=`uname`
-install_tmp=${rootPath}/tmp/mw_install.pl
myDir=${serverPath}/source/mysql-yum
ARCH=`uname -m`
@@ -118,7 +117,7 @@ rm -rf ${myDir}
Install_mysql()
{
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
mkdir -p $serverPath/mysql-yum
@@ -134,14 +133,14 @@ Install_mysql()
rm -rf $myDir
echo '5.7' > $serverPath/mysql-yum/version.pl
- echo '安装完成' > $install_tmp
+ echo '安装完成'
}
Uninstall_mysql()
{
YUM_UNINSTALL
rm -rf $serverPath/mysql-yum
- echo '卸载完成' > $install_tmp
+ echo '卸载完成'
}
action=$1
diff --git a/plugins/mysql-yum/versions/8.0/install.sh b/plugins/mysql-yum/versions/8.0/install.sh
index 1a3319f3d..27e72cbc4 100755
--- a/plugins/mysql-yum/versions/8.0/install.sh
+++ b/plugins/mysql-yum/versions/8.0/install.sh
@@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-
-install_tmp=${rootPath}/tmp/mw_install.pl
myDir=${serverPath}/source/mysql-yum
ARCH=`uname -m`
@@ -114,7 +112,7 @@ rm -rf ${myDir}
Install_mysql()
{
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
mkdir -p $serverPath/mysql-yum
mkdir -p /var/run/mysqld
diff --git a/plugins/mysql-yum/versions/8.2/install.sh b/plugins/mysql-yum/versions/8.2/install.sh
index 242b0f9a1..08cb36d1e 100755
--- a/plugins/mysql-yum/versions/8.2/install.sh
+++ b/plugins/mysql-yum/versions/8.2/install.sh
@@ -13,7 +13,6 @@ serverPath=$(dirname "$rootPath")
sysName=`uname`
-install_tmp=${rootPath}/tmp/mw_install.pl
myDir=${serverPath}/source/mysql-yum
ARCH=`uname -m`
@@ -115,7 +114,7 @@ rm -rf ${myDir}
Install_mysql()
{
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
mkdir -p $serverPath/mysql-yum
diff --git a/plugins/mysql-yum/versions/8.3/install.sh b/plugins/mysql-yum/versions/8.3/install.sh
index 50ffd5bdd..f7630ad86 100755
--- a/plugins/mysql-yum/versions/8.3/install.sh
+++ b/plugins/mysql-yum/versions/8.3/install.sh
@@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-
-install_tmp=${rootPath}/tmp/mw_install.pl
myDir=${serverPath}/source/mysql-yum
ARCH=`uname -m`
@@ -115,7 +113,7 @@ rm -rf ${myDir}
Install_mysql()
{
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
mkdir -p $serverPath/mysql-yum
diff --git a/plugins/mysql-yum/versions/8.4/install.sh b/plugins/mysql-yum/versions/8.4/install.sh
index 9c80cb9bc..02bf03981 100755
--- a/plugins/mysql-yum/versions/8.4/install.sh
+++ b/plugins/mysql-yum/versions/8.4/install.sh
@@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-
-install_tmp=${rootPath}/tmp/mw_install.pl
myDir=${serverPath}/source/mysql-yum
ARCH=`uname -m`
@@ -118,7 +116,7 @@ rm -rf ${myDir}
Install_mysql()
{
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
mkdir -p $serverPath/mysql-yum
mkdir -p /var/lib/mysql
diff --git a/plugins/mysql-yum/versions/9.0/install.sh b/plugins/mysql-yum/versions/9.0/install.sh
index 69c82c001..821b6c488 100755
--- a/plugins/mysql-yum/versions/9.0/install.sh
+++ b/plugins/mysql-yum/versions/9.0/install.sh
@@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-
-install_tmp=${rootPath}/tmp/mw_install.pl
myDir=${serverPath}/source/mysql-yum
ARCH=`uname -m`
@@ -119,7 +117,7 @@ rm -rf ${myDir}
Install_mysql()
{
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
mkdir -p $serverPath/mysql-yum
mkdir -p /var/lib/mysql
diff --git a/plugins/mysql/index.py b/plugins/mysql/index.py
index 885536eb3..0cdc59f5c 100755
--- a/plugins/mysql/index.py
+++ b/plugins/mysql/index.py
@@ -10,11 +10,12 @@ import re
import json
-# reload(sys)
-# sys.setdefaultencoding('utf-8')
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
-sys.path.append(os.getcwd() + "/class/core")
-import mw
+import core.mw as mw
if mw.isAppleSystem():
@@ -138,7 +139,7 @@ def getInitdTpl(version=''):
def contentReplace(content):
service_path = mw.getServerDir()
- content = content.replace('{$ROOT_PATH}', mw.getRootDir())
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$SERVER_APP_PATH}', service_path + '/mysql')
@@ -953,7 +954,7 @@ def __createUser(dbname, username, password, address):
def getDbBackupListFunc(dbname=''):
- bkDir = mw.getRootDir() + '/backup/database'
+ bkDir = mw.getBackupDir() + '/database'
blist = os.listdir(bkDir)
r = []
@@ -972,7 +973,7 @@ def setDbBackup():
if not data[0]:
return data[1]
- scDir = mw.getRunDir() + '/scripts/backup.py'
+ scDir = mw.getPanelDir() + '/scripts/backup.py'
cmd = 'python3 ' + scDir + ' database ' + args['name'] + ' 3'
os.system(cmd)
return mw.returnJson(True, 'ok')
@@ -1010,7 +1011,7 @@ def importDbExternal():
file = args['file']
name = args['name']
- import_dir = mw.getRootDir() + '/backup/import/'
+ import_dir = mw.getFatherDir() + '/backup/import/'
file_path = import_dir + file
if not os.path.exists(file_path):
@@ -1095,7 +1096,7 @@ def importDbExternalProgressBar():
file = args['file']
name = args['name']
- import_dir = mw.getRootDir() + '/backup/import/'
+ import_dir = mw.getFatherDir() + '/backup/import/'
file_path = import_dir + file
if not os.path.exists(file_path):
@@ -1158,11 +1159,11 @@ def importDbBackup():
file = args['file']
name = args['name']
- file_path = mw.getRootDir() + '/backup/database/' + file
- file_path_sql = mw.getRootDir() + '/backup/database/' + file.replace('.gz', '')
+ file_path = mw.getFatherDir() + '/backup/database/' + file
+ file_path_sql = mw.getFatherDir() + '/backup/database/' + file.replace('.gz', '')
if not os.path.exists(file_path_sql):
- cmd = 'cd ' + mw.getRootDir() + '/backup/database && gzip -d ' + file
+ cmd = 'cd ' + mw.getFatherDir() + '/backup/database && gzip -d ' + file
mw.execShell(cmd)
local_mode = recognizeDbMode()
@@ -1209,11 +1210,11 @@ def importDbBackupProgressBar():
file = args['file']
name = args['name']
- file_path = mw.getRootDir() + '/backup/database/' + file
- file_path_sql = mw.getRootDir() + '/backup/database/' + file.replace('.gz', '')
+ file_path = mw.getFatherDir() + '/backup/database/' + file
+ file_path_sql = mw.getFatherDir() + '/backup/database/' + file.replace('.gz', '')
if not os.path.exists(file_path_sql):
- cmd = 'cd ' + mw.getRootDir() + '/backup/database && gzip -d ' + file
+ cmd = 'cd ' + mw.getFatherDir() + '/backup/database && gzip -d ' + file
mw.execShell(cmd)
local_mode = recognizeDbMode()
@@ -1240,7 +1241,7 @@ def deleteDbBackup():
path = args['path']
full_file = ""
- bkDir = mw.getRootDir() + '/backup/database'
+ bkDir = mw.getFatherDir() + '/backup/database'
full_file = bkDir + '/' + args['filename']
if path != "":
full_file = path + "/" + args['filename']
@@ -1255,7 +1256,7 @@ def getDbBackupList():
return data[1]
r = getDbBackupListFunc(args['name'])
- bkDir = mw.getRootDir() + '/backup/database'
+ bkDir = mw.getFatherDir() + '/backup/database'
rr = []
for x in range(0, len(r)):
p = bkDir + '/' + r[x]
@@ -1278,7 +1279,7 @@ def getDbBackupList():
def getDbBackupImportList():
- bkImportDir = mw.getRootDir() + '/backup/import'
+ bkImportDir = mw.getFatherDir() + '/backup/import'
if not os.path.exists(bkImportDir):
os.mkdir(bkImportDir)
@@ -2779,7 +2780,7 @@ def trySlaveSyncBugfix(version=''):
def getSlaveSyncCmd(version=''):
- root = mw.getRunDir()
+ root = mw.getPanelDir()
cmd = 'cd ' + root + ' && python3 ' + root + \
'/plugins/mysql/index.py do_full_sync {"db":"all","sign",""}'
return mw.returnJson(True, 'ok', cmd)
@@ -3727,7 +3728,7 @@ def fullSync(version=''):
status_file = asyncTmpfile()
if args['begin'] == '1':
- cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + getPluginDir() + \
+ cmd = 'cd ' + mw.getPanelDir() + ' && python3 ' + getPluginDir() + \
'/index.py do_full_sync {"db":"' + \
args['db'] + '","sign":"' + sign + '"} &'
# print(cmd)
@@ -3773,8 +3774,9 @@ def uninstallPreInspection(version):
if mw.isDebugMode():
return 'ok'
- import plugins_api
- plugins_api.plugins_api().removeIndex(getPluginName(), version)
+
+ from utils.plugin import plugin as MwPlugin
+ MwPlugin.instance().removeIndex(getPluginName(), version)
return "请手动删除MySQL[{}]
rm -rf {}".format(version, getServerDir())
diff --git a/plugins/mysql/index_mysql.py b/plugins/mysql/index_mysql.py
index 791d9038f..d06228d4b 100644
--- a/plugins/mysql/index_mysql.py
+++ b/plugins/mysql/index_mysql.py
@@ -8,8 +8,12 @@ import subprocess
import re
import json
-sys.path.append(os.getcwd() + "/class/core")
-import mw
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
if mw.isAppleSystem():
@@ -48,7 +52,7 @@ def getConf():
def getDataDir():
file = getConf()
content = mw.readFile(file)
- rep = 'datadir\s*=\s*(.*)'
+ rep = r'datadir\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
@@ -56,7 +60,7 @@ def getDataDir():
def getRelayLogName():
file = getConf()
content = mw.readFile(file)
- rep = 'relay-log\s*=\s*(.*)'
+ rep = r'relay-log\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
@@ -64,7 +68,7 @@ def getRelayLogName():
def getLogBinName():
file = getConf()
content = mw.readFile(file)
- rep = 'log-bin\s*=\s*(.*)'
+ rep = r'log-bin\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
diff --git a/plugins/mysql/install.sh b/plugins/mysql/install.sh
index 5e9f903cc..2f0a73f67 100755
--- a/plugins/mysql/install.sh
+++ b/plugins/mysql/install.sh
@@ -16,10 +16,6 @@ rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
-
-install_tmp=${rootPath}/tmp/mw_install.pl
-
-
action=$1
type=$2
@@ -32,12 +28,12 @@ else
fi
if [ "${2}" == "" ];then
- echo '缺少安装脚本...' > $install_tmp
+ echo '缺少安装脚本...'
exit 0
fi
if [ ! -d $curPath/versions/$2 ];then
- echo '缺少安装脚本2...' > $install_tmp
+ echo '缺少安装脚本2...'
exit 0
fi
diff --git a/plugins/mysql/js/mysql.js b/plugins/mysql/js/mysql.js
index dcb54e786..c5c25eae6 100755
--- a/plugins/mysql/js/mysql.js
+++ b/plugins/mysql/js/mysql.js
@@ -2041,7 +2041,7 @@ function getFullSyncStatus(db){
myPostN('full_sync_cmd', {'db':db,'sign':''}, function(rdata){
var rdata = $.parseJSON(rdata.data);
layer.open({
- title: "手动执行命令CMD",
+ title: "手动执行命令CMD",
area: ['600px', '180px'],
type:1,
closeBtn: 1,
diff --git a/plugins/mysql/versions/5.5/install.sh b/plugins/mysql/versions/5.5/install.sh
index 22fc66e21..829de8864 100755
--- a/plugins/mysql/versions/5.5/install.sh
+++ b/plugins/mysql/versions/5.5/install.sh
@@ -12,7 +12,6 @@ serverPath=$(dirname "$rootPath")
sysName=`uname`
sysArch=`arch`
-install_tmp=${rootPath}/tmp/mw_install.pl
mysqlDir=${serverPath}/source/mysql
VERSION=5.5.62
@@ -60,7 +59,7 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -
Install_mysql()
{
mkdir -p ${mysqlDir}
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
if [ "$sysName" != "Darwin" ];then
mkdir -p /var/log/mariadb
@@ -164,7 +163,7 @@ Install_mysql()
Uninstall_mysql()
{
rm -rf $serverPath/mysql
- echo '卸载完成' > $install_tmp
+ echo '卸载完成'
}
action=$1
diff --git a/plugins/mysql/versions/5.6/install.sh b/plugins/mysql/versions/5.6/install.sh
index e05648ccb..43f5d15f9 100755
--- a/plugins/mysql/versions/5.6/install.sh
+++ b/plugins/mysql/versions/5.6/install.sh
@@ -53,14 +53,13 @@ fi
VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
-install_tmp=${rootPath}/tmp/mw_install.pl
mysqlDir=${serverPath}/source/mysql
VERSION=5.6.51
Install_mysql()
{
mkdir -p ${mysqlDir}
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
if [ "$sysName" != "Darwin" ];then
mkdir -p /var/log/mariadb
diff --git a/plugins/mysql/versions/5.7/install.sh b/plugins/mysql/versions/5.7/install.sh
index 98ac7954d..90d7bc303 100755
--- a/plugins/mysql/versions/5.7/install.sh
+++ b/plugins/mysql/versions/5.7/install.sh
@@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-
-install_tmp=${rootPath}/tmp/mw_install.pl
mysqlDir=${serverPath}/source/mysql
VERSION=5.7.44
@@ -61,7 +59,7 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -
Install_mysql()
{
mkdir -p ${mysqlDir}
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
if [ "$sysName" != "Darwin" ];then
mkdir -p /var/log/mariadb
diff --git a/plugins/mysql/versions/8.0/install.sh b/plugins/mysql/versions/8.0/install.sh
index a407718c2..31c3da456 100755
--- a/plugins/mysql/versions/8.0/install.sh
+++ b/plugins/mysql/versions/8.0/install.sh
@@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-
-install_tmp=${rootPath}/tmp/mw_install.pl
mysqlDir=${serverPath}/source/mysql
_os=`uname`
@@ -60,7 +58,7 @@ VERSION=8.0.37
Install_mysql()
{
mkdir -p ${mysqlDir}
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
# ----- cpu start ------
if [ -z "${cpuCore}" ]; then
diff --git a/plugins/mysql/versions/8.2/install.sh b/plugins/mysql/versions/8.2/install.sh
index 5bb6b6f88..6aa8fce4f 100755
--- a/plugins/mysql/versions/8.2/install.sh
+++ b/plugins/mysql/versions/8.2/install.sh
@@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-
-install_tmp=${rootPath}/tmp/mw_install.pl
mysqlDir=${serverPath}/source/mysql
_os=`uname`
@@ -60,7 +58,7 @@ VERSION=8.2.0
Install_mysql()
{
mkdir -p ${mysqlDir}
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
# ----- cpu start ------
if [ -z "${cpuCore}" ]; then
diff --git a/plugins/mysql/versions/8.3/install.sh b/plugins/mysql/versions/8.3/install.sh
index 20e9b9dff..a80545d43 100755
--- a/plugins/mysql/versions/8.3/install.sh
+++ b/plugins/mysql/versions/8.3/install.sh
@@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-
-install_tmp=${rootPath}/tmp/mw_install.pl
mysqlDir=${serverPath}/source/mysql
_os=`uname`
@@ -60,7 +58,7 @@ VERSION=8.3.0
Install_mysql()
{
mkdir -p ${mysqlDir}
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
# ----- cpu start ------
if [ -z "${cpuCore}" ]; then
diff --git a/plugins/mysql/versions/8.4/install.sh b/plugins/mysql/versions/8.4/install.sh
index b93081973..f5a51f5fe 100755
--- a/plugins/mysql/versions/8.4/install.sh
+++ b/plugins/mysql/versions/8.4/install.sh
@@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
-
-install_tmp=${rootPath}/tmp/mw_install.pl
mysqlDir=${serverPath}/source/mysql
_os=`uname`
@@ -62,7 +60,7 @@ VERSION=8.4.2
Install_mysql()
{
mkdir -p ${mysqlDir}
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
# ----- cpu start ------
diff --git a/plugins/mysql/versions/9.0/install.sh b/plugins/mysql/versions/9.0/install.sh
index 9699b7e3c..a23fbeca3 100755
--- a/plugins/mysql/versions/9.0/install.sh
+++ b/plugins/mysql/versions/9.0/install.sh
@@ -13,7 +13,6 @@ serverPath=$(dirname "$rootPath")
sysName=`uname`
-install_tmp=${rootPath}/tmp/mw_install.pl
mysqlDir=${serverPath}/source/mysql
_os=`uname`
@@ -62,7 +61,7 @@ VERSION=9.0.1
Install_mysql()
{
mkdir -p ${mysqlDir}
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
# ----- cpu start ------
diff --git a/plugins/nezha/index.py b/plugins/nezha/index.py
index 7f4dc3db4..d79ad4bc2 100755
--- a/plugins/nezha/index.py
+++ b/plugins/nezha/index.py
@@ -10,8 +10,12 @@ import json
from datetime import datetime
-sys.path.append(os.getcwd() + "/class/core")
-import mw
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
app_debug = False
if mw.isAppleSystem():
@@ -115,7 +119,7 @@ class App:
def contentReplace(self, content):
service_path = mw.getServerDir()
- content = content.replace('{$ROOT_PATH}', mw.getRootDir())
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$RUN_USER}', self.getRunUser())
content = content.replace('{$HOME_DIR}', self.getHomeDir())
diff --git a/plugins/nezha/install.sh b/plugins/nezha/install.sh
index 01b4af5bf..0a007b9a4 100755
--- a/plugins/nezha/install.sh
+++ b/plugins/nezha/install.sh
@@ -7,18 +7,16 @@ rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
-install_tmp=${rootPath}/tmp/mw_install.pl
-
action=$1
type=$2
if [ "${2}" == "" ];then
- echo '缺少安装脚本...' > $install_tmp
+ echo '缺少安装脚本...'
exit 0
fi
if [ ! -d $curPath/versions/$2 ];then
- echo '缺少安装脚本2...' > $install_tmp
+ echo '缺少安装脚本2...'
exit 0
fi
diff --git a/plugins/nezha/versions/0.15.2/install.sh b/plugins/nezha/versions/0.15.2/install.sh
index f845ca7dc..4ed401a67 100755
--- a/plugins/nezha/versions/0.15.2/install.sh
+++ b/plugins/nezha/versions/0.15.2/install.sh
@@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
-install_tmp=${rootPath}/tmp/mw_install.pl
VERSION=0.15.2
# bash install.sh install 0.15.2
@@ -82,7 +81,7 @@ download_file() {
Install_dashborad(){
- echo '正在安装哪吒监控...' > $install_tmp
+ echo '正在安装哪吒监控...'
mkdir -p $serverPath/source
if [ ! -f $TARGET_DIR/nezha ];then
@@ -103,7 +102,7 @@ Install_dashborad(){
}
Install_agent(){
- echo -e "正在下载监控端" > $install_tmp
+ echo -e "正在下载监控端"
mkdir -p $serverPath/source
version=v0.15.1
@@ -137,7 +136,7 @@ Install_App()
echo "$VERSION" > $serverPath/nezha/version.pl
cd ${rootPath} && python3 ${rootPath}/plugins/nezha/index.py init_cfg
fi
- echo 'install successful' > $install_tmp
+ echo 'install successful'
}
Uninstall_App()
@@ -147,7 +146,7 @@ Uninstall_App()
cd ${rootPath} && python3 ${rootPath}/plugins/nezha/index.py stop
cd ${rootPath} && python3 ${rootPath}/plugins/nezha/index.py stop_agent
rm -rf $serverPath/nezha
- echo "install fail" > $install_tmp
+ echo "install fail"
}
action=$1
diff --git a/plugins/op_load_balance/index.py b/plugins/op_load_balance/index.py
index 59a646fb2..262a20b04 100755
--- a/plugins/op_load_balance/index.py
+++ b/plugins/op_load_balance/index.py
@@ -8,8 +8,12 @@ import subprocess
import json
import re
-sys.path.append(os.getcwd() + "/class/core")
-import mw
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
app_debug = False
@@ -73,7 +77,7 @@ def writeConf(data):
def contentReplace(content):
service_path = mw.getServerDir()
- content = content.replace('{$ROOT_PATH}', mw.getRootDir())
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$APP_PATH}', app_path)
return content
diff --git a/plugins/op_load_balance/install.sh b/plugins/op_load_balance/install.sh
index d78bf73fb..d34795d33 100755
--- a/plugins/op_load_balance/install.sh
+++ b/plugins/op_load_balance/install.sh
@@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
-install_tmp=${rootPath}/tmp/mw_install.pl
action=$1
version=$2
@@ -24,11 +23,11 @@ else
fi
Install_App(){
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
mkdir -p $serverPath/op_load_balance
echo "${version}" > $serverPath/op_load_balance/version.pl
cd ${rootPath} && python3 ${rootPath}/plugins/op_load_balance/index.py start
- echo 'install ok' > $install_tmp
+ echo '安装OP负载均衡成功!'
}
Uninstall_App(){
diff --git a/plugins/op_waf/index.py b/plugins/op_waf/index.py
index 0f14caaf8..92698b3e9 100755
--- a/plugins/op_waf/index.py
+++ b/plugins/op_waf/index.py
@@ -12,8 +12,12 @@ import subprocess
import json
import re
-sys.path.append(os.getcwd() + "/class/core")
-import mw
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
app_debug = False
@@ -245,7 +249,7 @@ def contentReplace(content):
service_path = mw.getServerDir()
waf_root = getServerDir()
waf_path = waf_root + "/waf"
- content = content.replace('{$ROOT_PATH}', mw.getRootDir())
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$WAF_PATH}', waf_path)
content = content.replace('{$WAF_ROOT}', waf_root)
diff --git a/plugins/op_waf/install.sh b/plugins/op_waf/install.sh
index e3a7950c7..dd59aff37 100755
--- a/plugins/op_waf/install.sh
+++ b/plugins/op_waf/install.sh
@@ -9,8 +9,6 @@ rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
-install_tmp=${rootPath}/tmp/mw_install.pl
-
action=$1
version=$2
sys_os=`uname`
@@ -28,7 +26,7 @@ fi
Install_App(){
- echo '正在安装脚本文件...' > $install_tmp
+ echo '正在安装脚本文件...'
mkdir -p $serverPath/source/op_waf
mkdir -p $serverPath/op_waf
@@ -114,7 +112,7 @@ Install_App(){
fi
echo "${version}" > $serverPath/op_waf/version.pl
- echo 'install ok' > $install_tmp
+ echo '安装OP防火墙成功!'
cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py start
echo "cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py start"
diff --git a/plugins/op_waf/tool_task.py b/plugins/op_waf/tool_task.py
index 1ee26c173..70d2fd107 100644
--- a/plugins/op_waf/tool_task.py
+++ b/plugins/op_waf/tool_task.py
@@ -6,8 +6,13 @@ import os
import time
import json
-sys.path.append(os.getcwd() + "/class/core")
-import mw
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
+from utils.crontab import crontab as MwCrontab
app_debug = False
@@ -33,10 +38,9 @@ def getTaskConf():
def getConfigData():
- try:
+ conf = getTaskConf()
+ if os.path.exists(conf):
return json.loads(mw.readFile(getTaskConf()))
- except:
- pass
return []
@@ -89,7 +93,7 @@ def createBgTaskByName(name, args):
_where1 = args['minute-n']
_minute = ''
- mw_dir = mw.getRunDir()
+ mw_dir = mw.getPanelDir()
cmd = '''
mw_dir=%s
rname=%s
@@ -122,10 +126,10 @@ logs_file=$plugin_path/${rname}.log
'stype': "toShell",
'sname': '',
'sbody': cmd,
- 'urladdress': '',
+ 'url_address': '',
}
- task_id = cron_api.add(params)
+ task_id = MwCrontab.instance().add(params)
if task_id > 0:
cfg["task_id"] = task_id
cfg["name"] = name
@@ -146,9 +150,7 @@ def removeBgTask():
res = mw.M("crontab").field("id, name").where(
"id=?", (cfg["task_id"],)).find()
if res and res["id"] == cfg["task_id"]:
- import crontab_api
- api = crontab_api.crontab_api()
- data = api.delete(cfg["task_id"])
+ data = MwCrontab.instance().delete(cfg["task_id"])
if data[0]:
cfg["task_id"] = -1
cfg_list[x] = cfg
diff --git a/plugins/openresty/index.html b/plugins/openresty/index.html
index 30803d9f1..086bec6de 100755
--- a/plugins/openresty/index.html
+++ b/plugins/openresty/index.html
@@ -1,5 +1,5 @@
\
- \
-
\
- \
- \
-
\
- ';
- tbody += ' ';
- }
- $('#logs_list').html(tbody);
-
- },'json');
-}
-
-function getTempAccessLogs(id){
-
- layer.open({
- area: ['700px', '250px'],
- title: '临时授权管理',
- closeBtn:1,
- shift: 0,
- type: 1,
- content: "' + (rdata.data[i]['type']) +' ';
- tbody += '' + rdata.data[i]['addtime'] +' ';
- tbody += ''+ rdata.data[i]['log'] +' ';
- tbody += '\
- \
-
\
- \
- \
- \
- 操作类型 操作时间 日志 ';
- tbody += ' ';
- }
-
- $('#temp_login_view_tbody').html(tbody);
- $('.temp_login_view_page').html(rdata.page);
- },'json');
-}
-
-function setStatusCode(o){
- var code = $(o).data('code');
- layer.open({
- type: 1,
- area: ['420px', '220px'],
- title: "设置未认证时的响应状态",
- closeBtn: 1,
- shift: 5,
- btn:['提交','关闭'],
- shadeClose: false,
- content: '' + (rdata.data[i]['login_addr']||'未登陆') +' ';
-
- tbody += '';
- switch (parseInt(rdata.data[i]['state'])) {
- case 0:
- tbody += '待使用';
- break;
- case 1:
- tbody += '已使用';
- break;
- case -1:
- tbody += '已过期';
- break;
- }
- tbody += ' ';
-
- tbody += '' + (getLocalTime(rdata.data[i]['login_time'])||'未登陆') +' ';
- tbody += '' + getLocalTime(rdata.data[i]['expire']) +' ';
-
- tbody += '';
-
- if (rdata.data[i]['state'] == '1' ){
- tbody += '操作日志';
- } else{
- tbody += '删除';
- }
-
- tbody += ' ';
-
- tbody += '
\
- \
- \
-
\
- \
- \
- \
- \
- 登录IP 状态 登录时间 过期时间 操作 \
-
\
- \
-
\
-
重置密钥后,已关联密钥产品,将失效,请重新添加新密钥至产品。',{title:'重置密钥',closeBtn:2,icon:13,cancel:function(){
- }}, function() {
- $.post('/config/get_auth_secret', {'reset':"1"},function(rdata){
- showMsg("接口密钥已生成,重置密钥后,已关联密钥产品,将失效,请重新添加新密钥至产品。", function(){
- $('input[name="secret"]').val(rdata.data['secret']);
- $('.qrcode').html('').qrcode({ text: rdata.data['url']});
- } ,{icon:1}, 2000);
- },'json');
- });
- });
- },
- });
-
- },'json');
-}
-
-function setAuthSecretApi(){
- var cfg_panel_auth = $('#cfg_panel_auth').prop("checked");
- $.post('/config/set_auth_secret', {'op_type':"2"},function(rdata){
- showMsg(rdata.msg, function(){
- if (rdata.data == 1){
- setAuthBind();
- }
- } ,{icon:rdata.status?1:2}, 1000);
- },'json');
-}
-
-function setBasicAuthTip(callback){
- var tip = layer.open({
- area: ['500px', '385px'],
- title: '开启BasicAuth认证提示',
- closeBtn:0,
- shift: 0,
- type: 1,
- content: '风险操作!此功能不懂请勿开启!
\
- \
-
\
- \
-
\
-
(每行1个)\
- \
- \
-
\
-
重置密钥后,已关联密钥产品,将失效,请重新添加新密钥至产品。',{title:'重置密钥',closeBtn:2,icon:13,cancel:function(){
- }}, function() {
- $.post('/config/set_panel_token', {'op_type':"1"},function(rdata){
- showMsg("接口密钥已生成,重置密钥后,已关联密钥产品,将失效,请重新添加新密钥至产品。", function(){
- $('input[name="token"]').val(rdata.data);
- } ,{icon:1}, 2000);
- },'json');
- });
- });
-
- $('.save_api').click(function(){
- var limit_addr = $('textarea[name="api_limit_addr"]').val();
- $.post('/config/set_panel_token', {'op_type':"3",'limit_addr':limit_addr},function(rdata){
- showMsg(rdata.msg, function(){
- } ,{icon:rdata.status?1:2}, 2000);
- },'json');
- });
- },
- });
- },'json');
-}
-
-
-function setPanelApi(){
- var cfg_panel_api = $('#cfg_panel_api').prop("checked");
- $.post('/config/set_panel_token', {'op_type':"2"},function(rdata){
- showMsg(rdata.msg, function(){
- if (rdata.status){
- showPanelApi();
- }
- } ,{icon:rdata.status?1:2}, 1000);
- },'json');
-}
diff --git a/route/static/app/control.js b/route/static/app/control.js
deleted file mode 100755
index 4b68cd96b..000000000
--- a/route/static/app/control.js
+++ /dev/null
@@ -1,867 +0,0 @@
-//默认显示7天周期图表
-
-setTimeout(function(){
- Wday(0,'getload');
-},100);
-setTimeout(function(){
- Wday(0,'cpu');
-},200);
-setTimeout(function(){
- Wday(0,'mem');
-},500);
-setTimeout(function(){
- Wday(0,'disk');
-},100);
-setTimeout(function(){
- Wday(0,'network');
-},1500);
-
-
-$(".searcTime .st").click(function(){
- var status = $(this).data('status');
- if (status == 'show'){
- $(this).next().hide();
- $(this).data('status','hide');
- } else{
- $(this).next().show();
- $(this).data('status','show');
- }
-});
-
-//自定义时间-切换
-$(".searcTime .st").hover(function(){
- $(this).data('status','show');
- $(this).next().show();
-},function(){
- // $(this).next().hide();
- // $(this).next().hover(function(){
- // $(this).show();
- // },function(){
- // $(this).hide();
- // })
-})
-
-$(".searcTime .gt").click(function(){
- $(this).addClass("on").siblings().removeClass("on");
-})
-
-
-//渲染日期时间范围 start
-var render_dlist = [
- 'loadbtn_rtime',
- 'cpubtn_rtime',
- 'membtn_rtime',
- 'diskbtn_rtime',
- 'networkbtn_rtime'
-];
-
-for (var i = 0; i < render_dlist.length; i++) {
-
- laydate.render({
- elem: '#'+render_dlist[i]
- ,type: 'datetime'
- ,range: true
- });
-
-
- var b = getBeforeDate(28).replaceAll('/','-') + " 00:00:00";
- var e = getBeforeDate(0).replaceAll('/','-') + " 23:59:59";
-
- $('#'+render_dlist[i]).val(b + ' - ' + e);
-}
-//渲染日期时间范围 end
-
-
-$('.sbtn').click(function(){
- $(".searcTime .st").next().hide();
-
- var rtime = $(this).parent().find(".rtime").val();
- var rarr = rtime.split(' - ');
-
- var b = (new Date(rarr[0]).getTime())/1000;
- var e = (new Date(rarr[1]).getTime())/1000;
-
- b = Math.round(b);
- e = Math.round(e);
-
- if ($(this).hasClass('loadbtn')){
- getload(b,e);
- } else if ($(this).hasClass('cpubtn')){
- cpu(b,e);
- }else if ($(this).hasClass('membtn')){
- mem(b,e);
- } else if ($(this).hasClass('diskbtn')){
- disk(b,e);
- }
-});
-
-//指定天数
-function Wday(day,name){
- var now = (new Date().getTime())/1000;
- if(day==0){
- var b = (new Date(getToday() + " 00:00:01").getTime())/1000;
- b = Math.round(b);
- var e = Math.round(now);
- }
- if(day==1){
- var b = (new Date(getBeforeDate(day) + " 00:00:01").getTime())/1000;
- var e = (new Date(getBeforeDate(day) + " 23:59:59").getTime())/1000;
- b = Math.round(b);
- e = Math.round(e);
- }
- else{
- var b = (new Date(getBeforeDate(day) + " 00:00:01").getTime())/1000;
- b = Math.round(b);
- var e = Math.round(now);
- }
- switch (name){
- case "cpu":
- cpu(b, e);
- break;
- case "mem":
- mem(b, e);
- break;
- case "disk":
- disk(b, e);
- break;
- case "network":
- network(b, e);
- break;
- case "getload":
- getload(b, e);
- break;
- }
-}
-
-function getToday(){
- var mydate = new Date();
- var str = "" + mydate.getFullYear() + "/";
- str += (mydate.getMonth()+1) + "/";
- str += mydate.getDate();
- return str;
-}
-
-getStatus();
-//取监控状态
-function getStatus(){
- loadT = layer.msg('正在读取,请稍候...',{icon:16,time:0})
- $.post('/system/set_control','type=-1',function(rdata){
- layer.close(loadT);
-
- if(rdata.status){
- $("#openJK").html("");
- } else {
- $("#openJK").html("");
- }
-
- if(rdata.stat_all_status){
- $("#statAll").html("");
- } else{
- $("#statAll").html("");
- }
-
- $("#save_day").val(rdata.day);
-
- },'json');
-}
-
-
-//设置监控状态
-function setControl(act, value=false){
-
- if (act == 'openjk'){
- var type = $("#ctswitch").prop('checked')?'0':'1';
- var day = $("#save_day").val();
- if(day < 1){
- layer.msg('保存天数不合法!',{icon:2});
- return;
- }
- } else if (act == 'stat'){
- var type = $("#stat_witch").prop('checked')?'2':'3';
- } else if (act == 'save_day'){
- var type = $("#ctswitch").prop('checked')?'1':'0';
- var day = $("#save_day").val();
-
- if(type == 0){
- layer.msg('先开启监控!',{icon:2});
- return;
- }
-
- if(day < 1){
- layer.msg('保存天数不合法!',{icon:2});
- return;
- }
- }
-
- loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0})
- $.post('/system/set_control','type='+type+'&day='+day,function(rdata){
- showMsg(rdata.msg, function(){
- layer.close(loadT);
- },{icon:rdata.status?1:2})
- },'json');
-}
-
-
-//清理记录
-function closeControl(){
- layer.confirm('您真的清空所有监控记录吗?',{title:'清空记录',icon:3,closeBtn:1}, function() {
- loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0})
- $.post('/system/set_control','type=del',function(rdata){
- showMsg(rdata.msg, function(){
- layer.close(loadT);
- },{icon:rdata.status?1:2})
- },'json');
- });
-}
-
-
-//定义周期时间
-function getBeforeDate(n){
- var n = n;
- var d = new Date();
- var year = d.getFullYear();
- var mon=d.getMonth()+1;
- var day=d.getDate();
- if(day <= n){
- if(mon>1) {
- mon = mon-1;
- } else {
- year = year-1;
- mon = 12;
- }
- }
- d.setDate(d.getDate()-n);
- year = d.getFullYear();
- mon=d.getMonth()+1;
- day=d.getDate();
- s = year+"/"+(mon<10?('0'+mon):mon)+"/"+(day<10?('0'+day):day);
- return s;
-}
-//cpu
-function cpu(b,e){
- $.get('/system/get_cpu_io?start='+b+'&end='+e,function(rdata){
- var myChartCpu = echarts.init(document.getElementById('cupview'));
- var xData = [];
- var yData = [];
- //var zData = [];
-
- for(var i = 0; i < rdata.length; i++){
- xData.push(rdata[i].addtime);
- yData.push(rdata[i].pro);
- //zData.push(rdata[i].mem);
- }
- option = {
- tooltip: {
- trigger: 'axis',
- axisPointer: { type: 'cross' },
- formatter: '{b}
{a}: {c}%'
- },
- xAxis: {
- type: 'category',
- boundaryGap: false,
- data: xData,
- axisLine:{ lineStyle:{ color:"#666"} }
- },
- yAxis: {
- type: 'value',
- name: lan.public.pre,
- boundaryGap: [0, '100%'],
- min:0,
- max: 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:'CPU',
- type:'line',
- smooth:true,
- symbol: 'none',
- sampling: 'average',
- itemStyle: { normal: { color: 'rgb(0, 153, 238)' } },
- data: yData
- }
- ]
- };
- myChartCpu.setOption(option);
- window.addEventListener("resize",function(){
- myChartCpu.resize();
- });
- },'json');
-}
-
-//内存
-function mem(b,e){
- $.get('/system/get_cpu_io?start='+b+'&end='+e,function(rdata){
- var myChartMen = echarts.init(document.getElementById('memview'));
- var xData = [];
- //var yData = [];
- var zData = [];
-
- for(var i = 0; i < rdata.length; i++){
- xData.push(rdata[i].addtime);
- //yData.push(rdata[i].pro);
- zData.push(rdata[i].mem);
- }
- option = {
- tooltip: {
- trigger: 'axis',
- axisPointer: { type: 'cross' },
- formatter: '{b}
{a}: {c}%'
- },
- xAxis: {
- type: 'category',
- boundaryGap: false,
- data: xData,
- axisLine:{ lineStyle:{ color:"#666" } }
- },
- yAxis: {
- type: 'value',
- name: lan.public.pre,
- boundaryGap: [0, '100%'],
- min:0,
- max: 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.index.process_mem,
- type:'line',
- smooth:true,
- symbol: 'none',
- sampling: 'average',
- itemStyle: { normal: { color: 'rgb(0, 153, 238)' } },
- data: zData
- }
- ]
- };
- myChartMen.setOption(option);
- window.addEventListener("resize",function(){
- myChartMen.resize();
- });
- },'json');
-}
-
-//磁盘io
-function disk(b,e){
- $.get('/system/get_disk_io?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(3));
- wData.push((rdata[i].write_bytes/1024/60).toFixed(3));
- 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:"时间:{b0}
{a0}: {c0} Kb/s
{a1}: {c1} Kb/s",
- },
- legend: {
- data:['读取字节数','写入字节数']
- },
- xAxis: {
- type: 'category',
- boundaryGap: false,
- data: xData,
- axisLine:{ lineStyle:{ color:"#666" }}
- },
- yAxis: {
- type: 'value',
- name: '单位: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:'读取字节数',
- type:'line',
- smooth:true,
- symbol: 'none',
- sampling: 'average',
- itemStyle: { normal: { color: 'rgb(255, 70, 131)' } },
- data: rData
- },
- {
- name:'写入字节数',
- 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();
- });
- },'json');
-}
-
-//网络Io
-function network(b,e){
- $.get('/system/get_network_io?start='+b+'&end='+e,function(rdata){
- var myChartNetwork = echarts.init(document.getElementById('network'));
- var aData = [];
- var bData = [];
- var cData = [];
- var dData = [];
- var xData = [];
- var yData = [];
- var zData = [];
-
- for(var i = 0; i < rdata.length; i++){
- aData.push(rdata[i].total_up);
- bData.push(rdata[i].total_down);
- cData.push(rdata[i].down_packets);
- dData.push(rdata[i].up_packets);
- xData.push(rdata[i].addtime);
- yData.push(rdata[i].up);
- zData.push(rdata[i].down);
- }
- option = {
- tooltip: {
- trigger: 'axis',
- axisPointer: { type: 'cross' }
- },
- legend: {
- data:[lan.index.net_up,lan.index.net_down]
- },
- 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.index.net_up,
- type:'line',
- smooth:true,
- symbol: 'none',
- sampling: 'average',
- itemStyle: { normal: { color: 'rgb(255, 140, 0)' } },
- data: yData
- },
- {
- name:lan.index.net_down,
- type:'line',
- smooth:true,
- symbol: 'none',
- sampling: 'average',
- itemStyle: { normal: { color: 'rgb(30, 144, 255)' } },
- data: zData
- }
- ]
- };
- myChartNetwork.setOption(option);
- window.addEventListener("resize",function(){
- myChartNetwork.resize();
- });
- },'json');
-}
-//负载
-function getload_old(b,e){
- $.get('/system/get_load_average?start='+b+'&end='+e,function(rdata){
- var myChartgetload = echarts.init(document.getElementById('getloadview'));
- var aData = [];
- var bData = [];
- var xData = [];
- var yData = [];
- var zData = [];
-
- for(var i = 0; i < rdata.length; i++){
- xData.push(rdata[i].addtime);
- yData.push(rdata[i].pro);
- zData.push(rdata[i].one);
- aData.push(rdata[i].five);
- bData.push(rdata[i].fifteen);
- }
- option = {
- tooltip: {
- trigger: 'axis'
- },
- calculable: true,
- legend: {
- data:['系统资源使用率','1分钟','5分钟','15分钟'],
- selectedMode: 'single',
- },
- xAxis: {
- type: 'category',
- boundaryGap: false,
- data: xData,
- axisLine:{
- lineStyle:{
- color:"#666"
- }
- }
- },
- yAxis: {
- type: 'value',
- name: '',
- 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:'系统资源使用率',
- type:'line',
- smooth:true,
- symbol: 'none',
- sampling: 'average',
- itemStyle: {
- normal: {
- color: 'rgb(255, 140, 0)'
- }
- },
- data: yData
- },
- {
- name:'1分钟',
- type:'line',
- smooth:true,
- symbol: 'none',
- sampling: 'average',
- itemStyle: {
- normal: {
- color: 'rgb(30, 144, 255)'
- }
- },
- data: zData
- },
- {
- name:'5分钟',
- type:'line',
- smooth:true,
- symbol: 'none',
- sampling: 'average',
- itemStyle: {
- normal: {
- color: 'rgb(0, 178, 45)'
- }
- },
- data: aData
- },
- {
- name:'15分钟',
- type:'line',
- smooth:true,
- symbol: 'none',
- sampling: 'average',
- itemStyle: {
- normal: {
- color: 'rgb(147, 38, 255)'
- }
- },
- data: bData
- }
- ]
- };
- myChartgetload.setOption(option);
- window.addEventListener("resize",function(){
- myChartgetload.resize();
- });
- },'json');
-}
-//系统负载
-function getload(b,e){
- $.get('/system/get_load_average?start='+b+'&end='+e,function(rdata){
- var myChartgetload = echarts.init(document.getElementById('getloadview'));
- var aData = [];
- var bData = [];
- var xData = [];
- var yData = [];
- var zData = [];
-
- for(var i = 0; i < rdata.length; i++){
- xData.push(rdata[i].addtime);
- yData.push(rdata[i].pro);
- zData.push(rdata[i].one);
- aData.push(rdata[i].five);
- bData.push(rdata[i].fifteen);
- }
- option = {
- animation: false,
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'cross'
- }
- },
- legend: {
- data:['1分钟','5分钟','15分钟'],
- right:'16%',
- top:'10px'
- },
- axisPointer: {
- link: {xAxisIndex: 'all'},
- lineStyle: {
- color: '#aaaa',
- width: 1
- }
- },
- grid: [{ // 直角坐标系内绘图网格
- top: '60px',
- left: '5%',
- right: '55%',
- width: '40%',
- height: 'auto'
- },
- {
- top: '60px',
- left: '55%',
- width: '40%',
- height: 'auto'
- }
- ],
- xAxis: [
-
- { // 直角坐标系grid的x轴
- type: 'category',
- axisLine: {
- lineStyle: {
- color: '#666'
- }
- },
- data: xData
- },
- { // 直角坐标系grid的x轴
- type: 'category',
- gridIndex: 1,
- axisLine: {
- lineStyle: {
- color: '#666'
- }
- },
- data: xData
- },
- ],
- yAxis: [{
- scale: true,
- name: '资源使用率%',
- splitLine: { // y轴网格显示
- show: true,
- lineStyle:{
- color:"#ddd"
- }
- },
- nameTextStyle: { // 坐标轴名样式
- color: '#666',
- fontSize: 12,
- align: 'left'
- },
- axisLine:{
- lineStyle:{
- color: '#666',
- }
- }
- },
- {
- scale: true,
- name: '负载详情',
- gridIndex: 1,
- splitLine: { // y轴网格显示
- show: true,
- lineStyle:{
- color:"#ddd"
- }
- },
- nameTextStyle: { // 坐标轴名样式
- color: '#666',
- fontSize: 12,
- align: 'left'
- },
- axisLine:{
- lineStyle:{
- color: '#666',
- }
- }
- },
- ],
- dataZoom: [{
- type: 'inside',
- start: 0,
- end: 100,
- xAxisIndex:[0,1],
- zoomLock:true
- }, {
- xAxisIndex: [0, 1],
- type: 'slider',
- 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
- },
- left:'5%',
- right:'5%'
- }],
- series: [
- {
- name: '资源使用率%',
- type: 'line',
- lineStyle: { normal: { width: 2, color: 'rgb(255, 140, 0)'}},
- itemStyle: {normal: { color: 'rgb(255, 140, 0)' }},
- data: yData
- },
- {
- xAxisIndex: 1,
- yAxisIndex: 1,
- name: '1分钟',
- type: 'line',
- lineStyle: { normal: {width: 2,color: 'rgb(30, 144, 255)' }},
- itemStyle: { normal: { color: 'rgb(30, 144, 255)'} },
- data: zData
- },
- {
- xAxisIndex: 1,
- yAxisIndex: 1,
- name: '5分钟',
- type: 'line',
- lineStyle: { normal: { width: 2, color: 'rgb(0, 178, 45)'} },
- itemStyle: { normal: { color: 'rgb(0, 178, 45)' } },
- data: aData
- },
- {
- xAxisIndex: 1,
- yAxisIndex: 1,
- name: '15分钟',
- type: 'line',
- lineStyle: { normal: { width: 2, color: 'rgb(147, 38, 255)'}},
- itemStyle: { normal: { color: 'rgb(147, 38, 255)' } },
- data: bData
- }
- ],
- textStyle: { color: '#666',fontSize: 12}
- }
- myChartgetload.setOption(option);
- window.addEventListener("resize",function(){
- myChartgetload.resize();
- })
- },'json');
-}
\ No newline at end of file
diff --git a/route/static/app/crontab.js b/route/static/app/crontab.js
deleted file mode 100755
index 8db9e9fe1..000000000
--- a/route/static/app/crontab.js
+++ /dev/null
@@ -1,1131 +0,0 @@
-var num = 0;
-//查看任务日志
-function getLogs(id){
-
- var reqTimer = null;
- var reqCount = 0;
-
- var tips = layer.msg('正在获取,请稍候...',{icon:16,time:0,shade: [0.3, '#000']});
- var req_log_args = 'id='+id;
- function requestLogs(layerIndex){
-
- $.post('/crontab/logs', req_log_args, function(rdata){
-
- if (reqCount == 0){
- layer.close(tips);
- }
-
- if(!rdata.status) {
- layer.close(layerIndex);
- layer.msg(rdata.msg,{icon:2, time:2000});
- clearInterval(reqTimer);
- return;
- };
-
- if (rdata.msg == ''){
- rdata.msg = '暂无数据!';
- }
-
- $("#crontab_log").html(rdata.msg);
- //滚动到最低
- var ob = document.getElementById('crontab_log');
- ob.scrollTop = ob.scrollHeight;
- reqCount++;
- },'json');
-
- }
-
-
- layer.open({
- type:1,
- title:"任务执行日志",
- area: ['60%','500px'],
- shadeClose:false,
- btn:["清空","关闭"],
- closeBtn:1,
- end: function(){
- if (reqTimer){
- clearInterval(reqTimer);
- }
- },
- content:' ";
- } else {
- for(var i=0;i"+lan.crontab.task_empty+" ";
- }
- }
- $('#cronbody').html(cbody);
- $('#softPage').html(rdata.list);
- },'json');
-}
-
-// 设置计划任务状态
-function setTaskStatus(id,status){
- var confirm = layer.confirm(status == '0'?'计划任务暂停后将无法继续运行,您真的要停用这个计划任务吗?':'该计划任务已停用,是否要启用这个计划任务', {title:'提示',icon:3,closeBtn:1},function(index) {
- if (index > 0) {
- var loadT = layer.msg('正在设置状态,请稍后...',{icon:16,time:0,shade: [0.3, '#000']});
- $.post('/crontab/set_cron_status',{id:id},function(rdata){
-
- if (!rdata.status){
- layer.msg(rdata.msg,{icon:rdata.status?1:2});
- return;
- }
-
- showMsg(rdata.msg,function(){
- layer.close(loadT);
- layer.close(confirm);
- getCronData(1);
- },{icon:rdata.status?1:2},2000);
-
- },'json');
- }
- });
-}
-
-//执行任务脚本
-function startTask(id){
- var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']});
- var data='id='+id;
- $.post('/crontab/start_task',data,function(rdata){
- showMsg(rdata.msg, function(){
- },{icon:rdata.status?1:2,time:2000});
- },'json');
-}
-
-
-//清空日志
-function closeLogs(id){
- var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']});
- var data='id='+id;
- $.post('/crontab/del_logs',data,function(rdata){
- showMsg(rdata.msg, function(){
- // layer.closeAll();
- },{icon:rdata.status?1:2,time:2000});
- },'json');
-}
-
-
-//删除
-function planDel(id,name){
- safeMessage(lan.get('del',[name]),'您确定要删除该任务吗?',function(){
- var load = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']});
- var data='id='+id;
- $.post('/crontab/del',data,function(rdata){
- showMsg(rdata.msg, function(){
- layer.closeAll();
- getCronData(1);
- },{icon:rdata.status?1:2,time:2000});
- },'json');
- });
-}
-
-function isURL(str_url){
- var strRegex = '^(https|http|ftp|rtsp|mms)?://.+';
- var re=new RegExp(strRegex);
- if (re.test(str_url)){
- return (true);
- }else{
- return (false);
- }
-}
-
-
-//提交
-function planAdd(){
- var name = $(".planname input[name='name']").val();
- if(name == ''){
- $(".planname input[name='name']").focus();
- layer.msg('任务名称不能为空!',{icon:2});
- return;
- }
- $("#cronConfig input[name='name']").val(name);
-
- var type = $(".plancycle").find("b").attr("val");
- $("#cronConfig input[name='type']").val(type);
-
-
- var is1;
- var is2 = 1;
- switch(type){
- case 'day-n':
- is1=31;
- break;
- case 'hour-n':
- is1=23;
- break;
- case 'minute-n':
- is1=59;
- break;
- case 'month':
- is1=31;
- break;
- }
-
- var where1 = $('#excode_week b').attr('val');
- $("#cronConfig input[name='where1']").val(where1);
-
- if(where1 > is1 || where1 < is2){
- $("#ptime input[name='where1']").focus();
- layer.msg('表单不合法,请重新输入!',{icon:2});
- return;
- }
-
- var hour = $("#ptime input[name='hour']").val();
- if(hour > 23 || hour < 0){
- $("#ptime input[name='hour']").focus();
- layer.msg('小时值不合法!',{icon:2});
- return;
- }
- $("#cronConfig input[name='hour']").val(hour);
- var minute = $("#ptime input[name='minute']").val();
- if(minute > 59 || minute < 0){
- $("#ptime input[name='minute']").focus();
- layer.msg('分钟值不合法!',{icon:2});
- return;
- }
- $("#cronConfig input[name='minute']").val(minute);
-
- var save = $("#save").val();
- if(save < 0){
- layer.msg('不能有负数!',{icon:2});
- return;
- }
-
- $("#cronConfig input[name='save']").val(save);
- $("#cronConfig input[name='week']").val($(".planweek").find("b").attr("val"));
-
- var sType = $(".planjs").find("b").attr("val");
- var sBody = encodeURIComponent($("#implement textarea[name='sBody']").val());
-
- if (sType == 'toShell'){
- if(sBody == ''){
- $("#implement textarea[name='sBody']").focus();
- layer.msg('脚本代码不能为空!',{icon:2});
- return;
- }
- }
-
- if(sType == 'toFile'){
- if($("#viewfile").val() == ''){
- layer.msg('请选择脚本文件!',{icon:2});
- return;
- }
- }
-
- var urladdress = $("#urladdress").val();
- if(sType == 'toUrl'){
- if(!isURL(urladdress)){
- layer.msg('URL地址不正确!',{icon:2});
- $("implement textarea[name='urladdress']").focus();
- return;
- }
- }
- // urladdress = encodeURIComponent(urladdress);
- $("#cronConfig input[name='urladdress']").val(urladdress);
- $("#cronConfig input[name='sType']").val(sType);
- $("#cronConfig textarea[name='sBody']").val(decodeURIComponent(sBody));
-
- if(sType == 'site' || sType == 'database' || sType == 'path'){
- var backupTo = $(".planBackupTo").find("b").attr("val");
- $("#backupTo").val(backupTo);
- }
-
- var sName = $("#sName").attr("val");
-
- // if(sName == 'backupAll'){
- // var alist = $("ul[aria-labelledby='backdata'] li a");
- // var dataList = new Array();
- // for(var i=1;i\
- "+rdata.data[i].name+" \
- "+status+" \
- "+rdata.data[i].type+" \
- "+rdata.data[i].cycle+" \
- "+cron_save +" \
- "+cron_backupto+" \
- "+rdata.data[i].addtime+" \
- \
- 执行 | \
- 编辑 | \
- 日志 | \
- 删除\
- \
- \
- ';
- }
- for(var i=0;i'+shortwebname+' \
- '+shortpath+' \
- '+toSize(rdata.dirs[i].size)+' \
- '+getLocalTime(rdata.dirs[i].time)+' \
- \
- '+lan.files.recycle_bin_re+'\
- | '+lan.files.recycle_bin_del+'\
- \
- \
- ';
-
- continue;
- }
- var shortwebname = rdata.files[i].name.replace(/'/,"\\'");
- var shortpath = rdata.files[i].dname;
- if(shortwebname.length > 20) shortwebname = shortwebname.substring(0, 20) + "...";
- if(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+'\
- \
- \
- '
- }
- $("#RecycleBody").html(body);
- return;
- break;
- case 2:
- for(var i=0;i'+shortwebname+' \
- '+shortpath+' \
- '+toSize(rdata.files[i].size)+' \
- '+getLocalTime(rdata.files[i].time)+' \
- \
- '+lan.files.recycle_bin_re+'\
- | '+lan.files.recycle_bin_del+'\
- \
- \
- '
- }
- $("#RecycleBody").html(body);
- return;
- break;
- case 3:
- for(var i=0;i'+shortwebname+' \
- '+shortpath+' \
- '+toSize(rdata.dirs[i].size)+' \
- '+getLocalTime(rdata.dirs[i].time)+' \
- \
- '+lan.files.recycle_bin_re+'\
- | '+lan.files.recycle_bin_del+'\
- \
- \
- ';
- }
- $("#RecycleBody").html(body);
- return;
- break;
- case 4:
- for(var i=0;i'+shortwebname+' \
- '+shortpath+' \
- '+toSize(rdata.files[i].size)+' \
- '+getLocalTime(rdata.files[i].time)+' \
- \
- '+lan.files.recycle_bin_re+'\
- | '+lan.files.recycle_bin_del+'\
- \
- \
- ';
- }
- }
- $("#RecycleBody").html(body);
- return;
- break;
- case 5:
- for(var i=0;i'+shortwebname+' \
- '+shortpath+' \
- '+toSize(rdata.files[i].size)+' \
- '+getLocalTime(rdata.files[i].time)+' \
- \
- '+lan.files.recycle_bin_re+'\
- | '+lan.files.recycle_bin_del+'\
- \
- \
- '
- }
- }
- $("#RecycleBody").html(body);
- return;
- }
-
-
- var tablehtml = ''+shortwebname+' \
- '+shortpath+' \
- '+toSize(rdata.files[i].size)+' \
- '+getLocalTime(rdata.files[i].time)+' \
- \
- '+lan.files.recycle_bin_re+'\
- | '+lan.files.recycle_bin_del+'\
- \
- \
- \
-
\
- \
- \
- '+body+'\
- 文件名 原位置 \
- 大小 删除时间 \
- 操作 \
- \
- ";
- } else {
- //图标展示
- $("#set_icon").addClass("active");
- $("#set_list").removeClass("active");
- body += "";
- }
- }
-
- for (var i = 0; i < rdata.FILES.length; i++) {
- if(rdata.FILES[i] == null) continue;
- var fmp = rdata.FILES[i].split(";");
- var bodyZip = '';
- var download = '';
- var cnametext =fmp[0] + fmp[5];
- fmp[0] = fmp[0].replace(/'/,"\\'");
-
- if(isChineseChar(cnametext)){
- if(cnametext.length>16){
- cnametext = cnametext.substring(0,16) + '...';
- }
- } else{
- if( cnametext.length > 48 ){
- cnametext = cnametext.substring(0,48) + '...';
- }
- }
-
- var displayCompress = 1;
- if(isCompressFile(fmp[0])){
- bodyZip = "解压 | ";
- }
-
- if(isText(fmp[0])){
- bodyZip = "编辑 | ";
- }
-
- if(isImage(fmp[0])){
- download = "预览 | ";
- } else {
- download = "下载 | ";
- }
-
- totalSize += parseInt(fmp[1]);
- if(getCookie("rank")=="a"){
- body += "\
- \
- " + cnametext + " \
- "+toSize(fmp[1])+" \
- "+getLocalTime(fmp[2])+" \
- "+fmp[3]+" \
- "+fmp[4]+" \
- \
- 复制 | \
- 剪切 | \
- 重命名 | \
- 权限 | \
- 压缩 | \
- 删除\
- \
- \
- ";
- }
- else{
- body += "";
- }
- }
- var dirInfo = '(共{1}个目录与{2}个文件,大小:'.replace('{1}',rdata.DIR.length+'').replace('{2}',rdata.FILES.length+'')+''
- + (toSize(totalSize))+'获取)';
- $("#dir_info").html(dirInfo);
- if( getCookie('rank') == 'a' ){
-
- // console.log(post['order']);
- var size_icon = '';
- if (post['order'] == 'size desc'){
- size_icon = '';
- } else if (post['order'] == 'size asc'){
- size_icon = '';
- } else {
- size_icon = '';
- }
-
- var mtime_icon = '';
- if (post['order'] == 'mtime desc'){
- mtime_icon = '';
- } else if (post['order'] == 'mtime asc'){
- mtime_icon = '';
- } else {
- mtime_icon = '';
- }
-
- var fname_icon = '';
- if (post['order'] == 'fname desc'){
- fname_icon = '';
- } else if (post['order'] == 'fname asc'){
- fname_icon = '';
- } else {
- fname_icon = '';
- }
-
- var tablehtml = '\
- " + cnametext + " \
- " + (toSize(fmp[1])) + " \
- " + ((fmp[2].length > 11)?fmp[2]:getLocalTime(fmp[2])) + " \
- "+fmp[3]+" \
- "+fmp[4]+" \
- \
- 复制 | \
- 剪切 | \
- 重命名 | \
- 权限 | \
- 压缩 | "
- + bodyZip
- + download
- + "删除\
- \
- \
- \
-
';
- $("#fileCon").removeClass("fileList").html(tablehtml);
- $("#tipTools").width($("#fileCon").width());
- } else {
- $("#fileCon").addClass("fileList").html(body);
- $("#tipTools").width($("#fileCon").width());
- }
- $("#DirPathPlace input").val(rdata.PATH);
- var BarTools = '\
- \
- \
- '+body+'\
- \
- 文件名'+fname_icon+' \
- 大小'+size_icon+' \
- 修改时间'+mtime_icon+' \
- 权限 \
- 所有者 \
- 操作 \
- '+result[i].filename+' '+toSize(result[i].size)+' '+getLocalTime(result[i].mtime)+' ';
- }
- var mbody = '文件名 大小 最后修改时间 \
- '+tbody+'\
- '+result[i].filename+' '+toSize(result[i].size)+' '+getLocalTime(result[i].mtime)+' ';
- }
- var mbody = '文件名 大小 最后修改时间 \
- '+tbody+'\
- '
- });
-}
-
-//获取文件数据
-function getFileBytes(fileName, fileSize){
- window.open('/files/download?filename='+encodeURIComponent(fileName));
-}
-
-
-//上传文件
-function uploadFiles(){
- var path = $("#DirPathPlace input").val()+"/";
- layer.open({
- type:1,
- closeBtn: 1,
- title:lan.files.up_title,
- area: ['500px','300px'],
- shadeClose:false,
- content:'
\
-
'+html+'
\
-
\
- \
- \
- 名称 状态 \
- \
- 启动SSH \
- \
- \
- \
- \
- \
- 禁止密码登陆 \
- \
- \
- \
- ";
- }
- $("#firewallBody").html(body);
- $("#firewallPage").html(data.page);
- },'json');
-}
-
-//添加放行
-function addAcceptPort(){
- var type = $("#firewalldType").val();
- var port = $("#AcceptPort").val();
- var ps = $("#Ps").val();
- var protocol = $('select[name="protocol"]').val();
- var action = "add_drop_address";
- if(type == 'port'){
- ports = port.split(':');
- for(var i=0;i" + data.data[i].id + " \
- " + data.data[i].protocol + " \
- " + (data.data[i].port.indexOf('.') == -1?'放行端口'+':['+data.data[i].port+']':'屏蔽IP'+':['+data.data[i].port+']') + " \
- " + status + " \
- " + data.data[i].addtime + " \
- " + data.data[i].ps + " \
- 删除 \
-
');
- $.post('/system/rememory', '', function(rdata) {
- var percent = getPercent(rdata.memRealUsed, rdata.memTotal);
- var memText = Math.round(rdata.memRealUsed) + "/" + Math.round(rdata.memTotal) + " (MB)";
- percent = Math.round(percent);
- $(".mem-release").find('.mask').css({ 'color': '#20a53a', 'font-size': '14px' }).html("" + lan.index.memre_ok);
- setCookie("mem-before", memText);
- var memNull = Math.round(getCookie("memRealUsed") - rdata.memRealUsed);
- setTimeout(function() {
- if (memNull > 0) {
- $(".mem-release").find('.mask').css({ 'color': '#20a53a', 'font-size': '14px', 'line-height': '22px', 'padding-top': '22px' }).html("" + lan.index.memre_ok_1 + "
" + memNull + "MB");
- } else {
- $(".mem-release").find('.mask').css({ 'color': '#20a53a', 'font-size': '14px' }).html("" + lan.index.memre_ok_2);
- }
- $(".mem-release").removeClass("mem-action");
- $("#memory").text(memText);
- setCookie("memRealUsed", rdata.memRealUsed);
- }, 1000);
- setTimeout(function() {
- $(".mem-release").find('.mask').removeAttr("style").html("" + percent + "%");
- $(".mem-release").find(".mem-re-min").show();
- }, 2000)
- },'json');
- }, 2000);
-}
-
-function getPercent(num, total) {
- num = parseFloat(num);
- total = parseFloat(total);
- if (isNaN(num) || isNaN(total)) {
- return "-";
- }
- return total <= 0 ? "0%" : (Math.round(num / total * 10000) / 100.00);
-}
-
-function getDiskInfo() {
- $.get('/system/disk_info', function(rdata) {
- var dBody;
- for (var i = 0; i < rdata.length; i++) {
- var LoadColor = setcolor(parseInt(rdata[i].size[3].replace('%', '')), false, 75, 90, 95);
-
- //判断inode信息是否存在
- var inodes = '';
- if ( typeof(rdata[i]['inodes']) !=='undefined' ){
- inodes = '' + rdata[i].path + '
' +
- '' + rdata[i].size[1] + '/' + rdata[i].size[0] + '
' +
- '
', { icon: 16, time: 0, shade: [0.3, "#000"] });
- $.get('/system?action=ClearSystem', function(rdata) {
- layer.close(loadT);
- layer.msg('清理完成,共清理[' + rdata[0] + ']个文件,释放[' + toSize(rdata[1]) + ']磁盘空间!', { icon: 1 });
- });
-}
-
-function setMemImg(info){
-
- var memRealUsed = toSize(info.memRealUsed);
- var memTotal = toSize(info.memTotal);
-
- var memRealUsedVal = memRealUsed.split(' ')[0];
- var memTotalVal = memTotal.split(' ')[0];
- var unit = memTotal.split(' ')[1];
-
- var mem_txt = memRealUsedVal + '/' + memTotalVal + ' ('+ unit +')';
- setCookie("mem-before", mem_txt);
- $("#memory").html(mem_txt);
-
- var memPre = Math.floor(info.memRealUsed / (info.memTotal / 100));
- $("#left").html(memPre);
- setcolor(memPre, "#left", 75, 90, 95);
-
- var memFree = info.memTotal - info.memRealUsed;
- if (memFree/(1024*1024) < 64) {
- $("#messageError").show();
- $("#messageError").append('' + wlist.data[i].body + btns + '
\
- \
-
\
- \
- 风险类型: 暴力破解 说明 \
- 累计遭遇攻击总数: ' + num + ' 详细(数据直接来源本服务器日志) \
- 风险等级: 较高风险 \
- 风险描述: ' + atxt + ' \
- \
- 可参考解决方案:
\
- \
- \
- \
- '+pn_checked+'经典LNMP
\
- " + plugin_list + "
\
-
";
- for (var i = 0; i < config.length; i++) {
- if (typeof config[i].data == "undefined") {
- return false;
- }
- _tips += ' ' + config[i].seriesName + ':' + config[i].data + ' '+ index.net.default_unit + '
'
- }
- return _tips;
- }
-
- },
- legend: {
- data: [lan.index.net_up, lan.index.net_down],
- bottom: '2%'
- },
- xAxis: {
- type: 'category',
- boundaryGap: false,
- data: index.net.data.xData,
- axisLine: {
- lineStyle: {
- color: "#666"
- }
- }
- },
- yAxis: {
- name: '单位 '+ index.net.default_unit,
- splitLine: {
- lineStyle: { color: "#eee" }
- },
- axisLine: {
- lineStyle: { color: "#666" }
- }
- },
- series: [{
- name: '上行',
- type: 'line',
- data: index.net.data.yData,
- smooth: true,
- showSymbol: false,
- symbol: 'circle',
- symbolSize: 6,
- areaStyle: {
- normal: {
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
- offset: 0,
- color: 'rgba(255, 140, 0,0.5)'
- }, {
- offset: 1,
- color: 'rgba(255, 140, 0,0.8)'
- }], false)
- }
- },
- itemStyle: {
- normal: {
- color: '#f7b851'
- }
- },
- lineStyle: {
- normal: {
- width: 1
- }
- }
- },
- {
- name: '下行',
- type: 'line',
- data: index.net.data.zData,
- smooth: true,
- showSymbol: false,
- symbol: 'circle',
- symbolSize: 6,
- areaStyle: {
- normal: {
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
- offset: 0,
- color: 'rgba(30, 144, 255,0.5)',
- }, {
- offset: 1,
- color: 'rgba(30, 144, 255,0.8)',
- }], false)
- }
- },
- itemStyle: {
- normal: {
- color: '#52a9ff',
- }
- },
- lineStyle: {
- normal: {
- width: 1,
- }
- }
- }]
- };
- return option;
- },
-
-
- add: function (up, down) {
- var _net = this;
- var limit = 8;
- var d = new Date()
- if (_net.data.xData.length >= limit) _net.data.xData.splice(0, 1);
- if (_net.data.yData.length >= limit) _net.data.yData.splice(0, 1);
- if (_net.data.zData.length >= limit) _net.data.zData.splice(0, 1);
-
- _net.data.xData.push(d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds());
-
- if (up>down){
- var upTmp = toSizePos(up);
- var upTmpSize = upTmp['name'].split(' ')[0];
- index.net.default_unit = upTmp['name'].split(' ')[1] + '/s';
-
- var downTmpSize = toSizePos(down,upTmp['pos'])['name'].split(' ')[0];
- // console.log('up',upTmp['pos'],toSizePos(down, upTmp['pos']),downTmpSize);
-
- _net.data.zData.push(downTmpSize);
- _net.data.yData.push(upTmpSize);
- } else {
-
- var downTmp = toSizePos(down);
- var downTmpSize = downTmp['name'].split(' ')[0];
- index.net.default_unit = downTmp['name'].split(' ')[1] + '/s';
-
- var upTmpSize = toSizePos(up, downTmp['pos'])['name'].split(' ')[0];
- // console.log('down',downTmp['pos'],toSizePos(up, downTmp['pos']),upTmpSize);
-
- _net.data.zData.push(downTmpSize);
- _net.data.yData.push(upTmpSize);
- }
-
- },
- renderSelect:function(net){
- // console.log(net);
- if (!index.net.init_select){
- var option = '';
- var network = net.network;
- var network_io_key = getCookie('network_io_key');
-
- for (var name in network) {
- if (name == 'ALL'){
- option += '';
- } else if (network_io_key == name){
- option += '';
- } else {
- option += '';
- }
- }
- $('select[name="network-io"]').html(option);
- index.net.init_select = true;
- }
- }
- },
-
- iostat:{
- table: null,
- data: {
- xData: [],
- yData: [],
- zData: [],
- tipsData: []
- },
- init_select:false,
- default_unit : 'MB/s',
- init:function(){
- for (var i = 8; i >= 0; i--) {
- var time = (new Date()).getTime();
- index.iostat.data.xData.push(index.common.format(time - (i * 3 * 1000)));
- index.iostat.data.yData.push(0);
- index.iostat.data.zData.push(0);
- index.iostat.data.tipsData.push({});
- }
-
- index.iostat.table = echarts.init(document.getElementById('ioStat'));
- var option = index.iostat.defaultOption();
- index.iostat.table.setOption(option);
-
- window.addEventListener("resize", function () {
- index.iostat.table.resize();
- });
- },
-
- render:function(){
- index.iostat.table.setOption({
- tooltip: {
- trigger: 'axis',
- formatter :function (config) {
- var _config = config, _tips = "时间:" + _config[0].axisValue + "
", options = {
- read_bytes: '读取字节数',
- read_count: '读取次数 ',
- read_merged_count: '合并读取次数',
- read_time: '读取延迟',
- write_bytes: '写入字节数',
- write_count: '写入次数',
- write_merged_count: '合并写入次数',
- write_time: '写入延迟',
- }, data = index.iostat.data.tipsData[config[0].dataIndex], list = ['read_count', 'write_count', 'read_merged_count', 'write_merged_count', 'read_time', 'write_time',];
- for (var i = 0; i < config.length; i++) {
- if (typeof config[i].data == "undefined") {
- return false;
- }
- _tips += ' ' + config[i].seriesName + ':' + config[i].data + ' MB/s' + '
'
- }
- $.each(list, function (index, item) {
-
- if (typeof data[item] != 'undefined'){
- _tips += ' ' + options[item] + ':' + data[item] + (item.indexOf('time') > -1 ? ' ms' : ' 次/秒') + '
';
- }
- })
- return _tips;
- }
- },
- yAxis: {
- name: '单位 '+ index.iostat.default_unit,
- splitLine: { lineStyle: { color: "#eee" } },
- axisLine: { lineStyle: { color: "#666" } }
- },
- xAxis: {
- data: index.iostat.data.xData
- },
- series: [{
- name: "读取",
- data: index.iostat.data.yData
- }, {
- name: "写入",
- data: index.iostat.data.zData
- }]
- });
- },
- defaultOption:function(){
- var option = {
- title: {
- text: "",
- left: 'center',
- textStyle: {
- color: '#888888',
- fontStyle: 'normal',
- fontFamily: "宋体",
- fontSize: 16,
- }
- },
- tooltip: {
- trigger: 'axis'
- },
- legend: {
- data: ["读取", "写入"],
- bottom: '2%'
- },
- xAxis: {
- type: 'category',
- boundaryGap: false,
- data: index.iostat.data.xData,
- axisLine: {
- lineStyle: {
- color: "#666"
- }
- }
- },
- yAxis: {
- name: '单位 '+ index.iostat.default_unit,
- splitLine: {
- lineStyle: { color: "#eee" }
- },
- axisLine: {
- lineStyle: { color: "#666" }
- }
- },
- series: [{
- name: '读取',
- type: 'line',
- data: index.iostat.data.yData,
- smooth: true,
- showSymbol: false,
- symbol: 'circle',
- areaStyle: {
- normal: {
- color: 'rgb(255, 70, 131)'
- }
- },
- itemStyle: {
- normal: {
- color: 'rgb(255, 70, 131)'
- }
- },
- lineStyle: {
- normal: {
- width: 1,
- }
- }
- },
- {
- name: '写入',
- type: 'line',
- data: index.iostat.data.zData,
- smooth: true,
- showSymbol: false,
- symbol: 'circle',
- symbolSize: 6,
- areaStyle: {
- normal: {
- color: 'rgba(46, 165, 186, .7)'
- }
- },
- itemStyle: {
- normal: {
- color: 'rgba(46, 165, 186, .7)'
- }
- },
- lineStyle: {
- normal: {
- width: 1,
- }
- }
- }]
- };
- return option;
- },
-
- renderSelect:function(data){
- if (!index.iostat.init_select){
- var option = '';
- var iostat = data.iostat;
- var disk_io_key = getCookie('disk_io_key');
-
- for (var name in iostat) {
- if (name == 'ALL'){
- option += '';
- } else if (disk_io_key == name){
- option += '';
- } else {
- option += '';
- }
- }
- $('select[name="disk-io"]').html(option);
- index.iostat.init_select = true;
- }
- },
- add: function (read, write, data) {
- var _iostat = this;
- var limit = 8;
- var d = new Date()
- if (_iostat.data.xData.length >= limit) _iostat.data.xData.splice(0, 1);
- if (_iostat.data.yData.length >= limit) _iostat.data.yData.splice(0, 1);
- if (_iostat.data.zData.length >= limit) _iostat.data.zData.splice(0, 1);
- if (_iostat.data.tipsData.length >= limit) _iostat.data.tipsData.splice(0, 1);
-
-
- var readTmpSize = toSizeMB(read).split(' ')[0];
- var writeTmpSize = toSizeMB(write).split(' ')[0];
-
- _iostat.data.zData.push(writeTmpSize);
- _iostat.data.yData.push(readTmpSize);
- _iostat.data.tipsData.push(data);
- _iostat.data.xData.push(d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds());
- },
-
- },
- getData:function(){
-
- $.get("/system/network", function(net) {
-
- //网络IO
- var network_io_key = getCookie('network_io_key');
- var network_data = net.network;
- var network_select = network_data['ALL'];
- if (network_io_key && network_io_key != ''){
- network_select = network_data[network_io_key];
- }
-
- index.net.add(network_select.up,network_select.down);
- index.net.render();
- index.net.renderSelect(net);
-
- $("#upSpeed").html(toSize(network_select.up));
- $("#downSpeed").html(toSize(network_select.down));
-
- $("#downAll").html(toSize(network_select.downTotal));
- $("#downAll").attr('title','数据包:' + network_select.downPackets)
- $("#upAll").html(toSize(network_select.upTotal));
- $("#upAll").attr('title','数据包:' + network_select.upPackets)
-
-
- //磁盘IO
- var disk_io_key = getCookie('disk_io_key');
- var iostat_data = net.iostat;
- var iostat_select = iostat_data['ALL'];
- if (disk_io_key && disk_io_key != ''){
- iostat_select = iostat_data[disk_io_key];
- }
-
- index.iostat.add(iostat_select.read_bytes,iostat_select.write_bytes, iostat_select);
- index.iostat.render();
- index.iostat.renderSelect(net);
-
- $("#readBytes").html(toSize(iostat_select.read_bytes));
- $("#writeBytes").html(toSize(iostat_select.write_bytes));
- $("#diskIops").html(iostat_select.read_count+":"+iostat_select.write_count+ " 次");
- $("#diskTime").html(iostat_select.read_time+":"+iostat_select.write_time +" ms");
-
-
- $("#core").html(net.cpu[1] + " " + lan.index.cpu_core);
- $("#state").html(net.cpu[0]);
-
- setcolor(net.cpu[0], "#state", 30, 70, 90);
- //负载
- getLoad(net.load);
- //内存
- setMemImg(net.mem);
- //绑定hover事件
- setImg();
- showCpuTips(net);
-
- },'json');
- },
- task:function(){
- // index.getData();
- setInterval(function() {
- index.getData();
- }, 3000);
- },
- init: function(){
- index.net.init();
- index.iostat.init();
- index.task();
- }
-}
diff --git a/route/static/app/logs.js b/route/static/app/logs.js
deleted file mode 100644
index c61f3d7b1..000000000
--- a/route/static/app/logs.js
+++ /dev/null
@@ -1,205 +0,0 @@
-
-$(document).ready(function(){
- logsLoad();
-});
-
-function changeLogsViewH(){
- var l = $(window).height();
- $('.container-fluid .tab-view-box').css('height',l-80-40);
-
- $('#panelLogs').css('height',l-80-40-50);
-
- $('#logAudit .logAuditTab').css('height',l-80-40-50);
- $('#logAudit .logAuditContent').css('height',l-80-40-50);
-
-}
-
-function logsLoad(){
- changeLogsViewH();
- $(window).resize(function(){
- changeLogsViewH();
- });
-
- getLogs(1);
-}
-
-
-
-$('#cutTab .tabs-item').click(function(){
- var type = $(this).data('name');
-
- $('#cutTab .tabs-item').removeClass('active');
- $(this).addClass('active');
-
-
- $('.tab-view-box .tab-con').addClass('hide').removeClass('show').removeClass('w-full');
- $('#'+type).addClass('show').addClass('w-full');
-
- switch(type){
- case 'panelLogs':
- getLogs(1);
- break;
- case 'logAudit':
- getAuditLogsFiles();
- break;
- }
-
-});
-
-
-$('#panelLogs .refresh').click(function(){
- getLogs(1);
-});
-
-$('#panelLogs .clear').click(function(){
- delLogs(1);
-});
-
-
-function getAuditLogsFiles(){
- var loadT = layer.msg('正在获取日志审计列表...', { icon: 16, time: 0, shade: 0.3 });
- $.post('/logs/get_audit_logs_files',{}, function(data) {
- layer.close(loadT);
- var option = '';
- for (var i = 0; i < data.length; i++) {
- var tip = data[i]['name'] +' - '+data[i]['title'] + '(' + toSize(data[i]['size']) + ')';
- if (i==0){
- option += '\
- \
-
\
- \
- \
- \
- \
- 用户 \
- 来源 \
- 端口 \
- 时间 \
- \
- \
- root \
- 117.139.193.29 \
- pts/0 \
- 2023-08-25 13:27 still logged in \
- \
- //
';
- $('#logAudit .logAuditContent').html(pre_html);
-
- if (plist.length>0){
- var tmp = plist[0];
- var thead = '';
- tbody += ' \
- // \
- // 时间 角色 事件 '
- for (var i in tmp) {
- tbody+=' ';
- $('#logAudit .logAuditContent thead').html(tbody);
- }
-
-
- var tbody = '';
- for (var i = 0; i < plist.length; i++) {
- tbody += ''+ i + ' ';
- }
- tbody += '';
- for (var vv in plist[i]) {
- tbody+= ' ';
- }
- $('#logAudit .logAuditContent tbody').html(tbody);
-
- $('#logAudit .refresh').click(function(){
- getAuditFile(log_name);
- });
- }
-
- if (typeof(data) == 'string'){
- var cc = ''+ plist[i][vv] + ' '
- }
- tbody += '\
- ";
- }
- $("#operationLog tbody").html(body);
- $("#panelLogs .page").html(data.page);
- },'json');
-}
-
-function delLogs(){
- layer.confirm('即将清空面板日志,继续吗?',{title:'清空日志',closeBtn:2},function(){
- var loadT = layer.msg('正在清理,请稍候...',{icon:16});
- $.post('/logs/del_panel_logs','',function(rdata){
- layer.close(loadT);
- layer.msg(rdata.msg,{icon:rdata.status?1:2});
- getLogs(1);
- },'json');
- });
-}
\ No newline at end of file
diff --git a/route/static/app/public.js b/route/static/app/public.js
deleted file mode 100755
index 56fb17891..000000000
--- a/route/static/app/public.js
+++ /dev/null
@@ -1,2784 +0,0 @@
-
-$(document).ready(function() {
- $(".sub-menu a.sub-menu-a").click(function() {
- $(this).next(".sub").slideToggle("slow").siblings(".sub:visible").slideUp("slow");
- });
-});
-
-function toSize(a) {
- var d = [" B", " KB", " MB", " GB", " TB", " PB"];
- var e = 1024;
- for(var b = 0; b < d.length; b++) {
- if(a < e) {
- return(b == 0 ? a : a.toFixed(2)) + d[b]
- }
- a /= e;
- }
-}
-
-function toSizePos(a, pos = 0) {
- var d = [" B", " KB", " MB", " GB", " TB", " PB"];
- var e = 1024;
- var r = {};
- for(var b = 0; b < d.length; b++) {
- if (pos > 0){
- if (b == pos){
- r['name'] = (b == 0 ? a : a.toFixed(2)) + d[b];
- r['pos'] = b;
- return r
- }
- } else {
- if( a < e) {
- r['name'] = (b == 0 ? a : a.toFixed(2)) + d[b];
- r['pos'] = b;
- return r
- }
- }
- a /= e;
- }
-}
-
-function toSizeMB(a) {
- var d = [" KB", " MB"];
- var e = 1024;
- var i = 0;
- for(var b = 0; b < d.length; b++) {
- a /= e;
- i = b;
- }
- return a.toFixed(2) + d[i]
-}
-
-function toTrim(x) {
- return x.replace(/^\s+|\s+$/gm,'');
-}
-
-function inArray(f, arr){
- for (var i = 0; i < arr.length; i++) {
- if (f == arr[i]) {
- return true;
- }
- }
- return false;
-}
-
-//表格头固定
-function tableFixed(name) {
- var tableName = document.querySelector('#' + name);
- tableName.addEventListener('scroll', scrollHandle);
-}
-
-function escapeHTML(a){
- a = "" + a;
- return a.replace(/&/g, "&").replace(//g, ">").replace(/"/g, '"').
- replace(/'/g,"‘").replace(/\(/g,"(").replace(/\</g,"<").
- replace(/\>/g,">").replace(/`/g,"`").replace(/=/g,"=");
-}
-
-function scrollHandle(e) {
- var scrollTop = this.scrollTop;
- //this.querySelector('thead').style.transform = 'translateY(' + scrollTop + 'px)';
- $(this).find("thead").css({ "transform": "translateY(" + scrollTop + "px)", "position": "relative", "z-index": "1" });
-}
-
-
-//转换单们到MB
-function toSizeM(byteLen) {
- var a = parseInt(byteLen) / 1024 / 1024;
- return a || 0;
-}
-
-//字节单位转换MB
-function toSizeG(bytes){
- var c = 1024 * 1024;
- var b = 0;
- if(bytes > 0){
- var b = (bytes/c).toFixed(2);
- }
- return b;
-}
-
-//to unixtime
-function toUnixTime(txt){
- var unix = new Date(Date.parse(txt.replace(/-/g,'/'))).getTime();
- return unix/1000;
- }
-
-function randomStrPwd(b) {
- b = b || 32;
- // &!@%
- var c = "AaBbCcDdEeFfGHhiJjKkLMmNnPpRSrTsWtXwYxZyz2345678";
- var a = c.length;
- var d = "";
- for(i = 0; i < b; i++) {
- d += c.charAt(Math.floor(Math.random() * a))
- }
- return d
-}
-
-function getRandomString(len) {
- len = len || 32;
- var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; // 默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1
- var maxPos = chars.length;
- var pwd = '';
- for (i = 0; i < len; i++) {
- pwd += chars.charAt(Math.floor(Math.random() * maxPos));
- }
- return pwd;
-}
-
-//验证IP地址
-function isValidIP(ip) {
- var 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])$/
- return reg.test(ip);
-}
-
-function isContains(str, substr) {
- return str.indexOf(substr) >= 0;
-}
-
-
-function filterPath(path){
- var path_arr = path.split('/');
- var path_new = [];
- for (var i = 0; i < path_arr.length; i++) {
- if (path_arr[i]!=''){
- path_new.push(path_arr[i]);
- }
- }
- var rdata = "/"+path_new.join('/');
- return rdata;
-}
-
-function msgTpl(msg, args){
- if (typeof args == 'string'){
- return msg.replace('{1}', args);
- } else if (typeof args == 'object'){
- for (var i = 0; i < args.length; i++) {
- rep = '{' + (i + 1) + '}';
- msg = msg.replace(rep, args[i]);
- }
- }
- return msg;
-}
-
-function refresh() {
- window.location.reload()
-}
-
-function mwsPost(path, args, callback){
- $.post(path, args, function(rdata){
- if(typeof(callback) == 'function'){
- callback(rdata);
- }
- },'json');
-}
-
-function syncPost(path, args){
- var retData;
- $.ajax({
- type : 'post',
- url : path,
- data : args,
- async : false,
- dataType:'json',
- success : function(data){
- retData = data;
- }
- });
- return retData;
-}
-
-function repeatPwd(a) {
- $("#MyPassword").val(randomStrPwd(a))
-}
-
-$(".menu-icon").click(function() {
- $(".sidebar-scroll").toggleClass("sidebar-close");
- $(".main-content").toggleClass("main-content-open");
- if($(".sidebar-close")) {
- $(".sub-menu").find(".sub").css("display", "none");
- }
-});
-
-var Upload, percentage;
-Date.prototype.format = function(b) {
- var c = {
- "M+": this.getMonth() + 1,
- "d+": this.getDate(),
- "h+": this.getHours(),
- "m+": this.getMinutes(),
- "s+": this.getSeconds(),
- "q+": Math.floor((this.getMonth() + 3) / 3),
- S: this.getMilliseconds()
- };
- if(/(y+)/.test(b)) {
- b = b.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length))
- }
- for(var a in c) {
- if(new RegExp("(" + a + ")").test(b)) {
- b = b.replace(RegExp.$1, RegExp.$1.length == 1 ? c[a] : ("00" + c[a]).substr(("" + c[a]).length))
- }
- }
- return b
-};
-
-function getLocalTime(a) {
- a = a.toString();
- if(a.length > 10) {
- a = a.substring(0, 10)
- }
- return new Date(parseInt(a) * 1000).format("yyyy/MM/dd hh:mm:ss")
-}
-
-function getFormatTime(tm, format) {
- if (format == undefined) format = "yyyy/MM/dd hh:mm:ss";
- tm = tm.toString();
- if (tm.length > 10) {
- tm = tm.substring(0, 10);
- }
- var data = new Date(parseInt(tm) * 1000);
- var o = {
- "M+": data.getMonth() + 1, //month
- "d+": data.getDate(), //day
- "h+": data.getHours(), //hour
- "m+": data.getMinutes(), //minute
- "s+": data.getSeconds(), //second
- "q+": Math.floor((data.getMonth() + 3) / 3), //quarter
- "S": data.getMilliseconds() //millisecond
- }
- if (/(y+)/.test(format)) format = format.replace(RegExp.$1,
- (data.getFullYear() + "").substr(4 - RegExp.$1.length));
- for (var k in o)
- if (new RegExp("(" + k + ")").test(format))
- format = format.replace(RegExp.$1,RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
- return format;
-}
-
-
-
-function changePathCallback(default_dir, callback) {
-
- var c = layer.open({
- type: 1,
- area: "650px",
- title: '选择目录',
- closeBtn: 1,
- shift: 5,
- shadeClose: false,
- content: "" + data.data[i].id + " \
- " + data.data[i].type + " \
- " + data.data[i].log + " \
- " + data.data[i].addtime + " \
-
\
- \
-
\
- \
-
\
- \
- \
- \
- \
- 文件名 \
- 修改时间 \
- 权限 \
- 所有者 \
- \
- \
-
\
- \
-
\
- \
- \
- \
- \
- 文件名 \
- 修改时间 \
- 权限 \
- 所有者 \
- \
- \
- ";
- }
- if(h.FILES != null && h.FILES != "") {
- for(var f = 0; f < h.FILES.length; f++) {
- var g = h.FILES[f].split(";");
- var e = g[0];
- if(isChineseChar(e)) {
- if(e.length > 10) {
- e = e.substring(0, 10) + "..."
- }
- } else{
- if(e.length > 20) {
- e = e.substring(0, 20) + "..."
- }
- }
-
- d += "\
- " + e + " " + getLocalTime(g[2]) + " \
- " + g[3] + " \
- " + g[4] + " \
- X \
- \
- ";
- }
- }
- $(".default").hide();
- $(".file-list").show();
- $("#tbody").html(d);
- if(h.PATH.substr(h.PATH.length - 1, 1) != "/") {
- h.PATH += "/";
- }
- $("#PathPlace").find("span").html(h.PATH);
- activeDisk();
- return;
- },'json');
-}
-
-function createFolder() {
- var a = "" + e + " \
- " + getLocalTime(g[2]) + " \
- " + g[3] + " \
- " + g[4] + " \
- \
- \
- ";
- if($("#tbody tr").length == 0) {
- $("#tbody").append(a)
- } else {
- $("#tbody tr:first-child").before(a)
- }
- $(".newFolderName").focus();
- $("#nameOk").click(function() {
- var c = $("#newFolderName").val();
- var b = $("#PathPlace").find("span").text();
- newTxt = b.replace(new RegExp(/(\/\/)/g), "/") + c;
- var d = "path=" + newTxt;
- $.post("/files/create_dir", d, function(e) {
- if(e.status == true) {
- layer.msg(e.msg, {
- icon: 1
- })
- } else {
- layer.msg(e.msg, {
- icon: 2
- })
- }
- getDiskList(b);
- },'json');
- });
- $("#nameNOk").click(function() {
- $(this).parents("tr").remove()
- })
-}
-
-function NewDelFile(c) {
- var a = $("#PathPlace").find("span").text();
- newTxt = c.replace(new RegExp(/(\/\/)/g), "/");
- var b = "path=" + newTxt + "&empty=True";
- $.post("/files/delete_dir", b, function(d) {
- if(d.status == true) {
- layer.msg(d.msg, {
- icon: 1
- })
- } else {
- layer.msg(d.msg, {
- icon: 2
- })
- }
- getDiskList(a);
- },'json');
-}
-
-function activeDisk() {
- var a = $("#PathPlace").find("span").text().substring(0, 1);
- switch(a) {
- case "C":
- $(".path-con-left dd:nth-of-type(1)").css("background", "#eee").siblings().removeAttr("style");
- break;
- case "D":
- $(".path-con-left dd:nth-of-type(2)").css("background", "#eee").siblings().removeAttr("style");
- break;
- case "E":
- $(".path-con-left dd:nth-of-type(3)").css("background", "#eee").siblings().removeAttr("style");
- break;
- case "F":
- $(".path-con-left dd:nth-of-type(4)").css("background", "#eee").siblings().removeAttr("style");
- break;
- case "G":
- $(".path-con-left dd:nth-of-type(5)").css("background", "#eee").siblings().removeAttr("style");
- break;
- case "H":
- $(".path-con-left dd:nth-of-type(6)").css("background", "#eee").siblings().removeAttr("style");
- break;
- default:
- $(".path-con-left dd").removeAttr("style")
- }
-}
-
-function backMyComputer() {
- $(".default").show();
- $(".file-list").hide();
- $("#PathPlace").find("span").html("");
- activeDisk();
-}
-
-function backFile() {
- var c = $("#PathPlace").find("span").text();
- if(c.substr(c.length - 1, 1) == "/") {
- c = c.substr(0, c.length - 1)
- }
- var d = c.split("/");
- var a = "";
- if(d.length > 1) {
- var e = d.length - 1;
- for(var b = 0; b < e; b++) {
- a += d[b] + "/"
- }
- getDiskList(a.replace("//", "/"))
- } else {
- a = d[0]
- }
- if(d.length == 1) {}
-}
-
-function getfilePath() {
- var a = $("#PathPlace").find("span").text();
- a = a.replace(new RegExp(/(\\)/g), "/");
- a_len = a.length;
- if (a[a_len-1] == '/'){
- a = a.substr(0,a_len-1);
- }
-
- $("#" + getCookie("SetId")).val(a + getCookie("SetName"));
- layer.close(getCookie("changePath"));
- return a;
-}
-
-function setCookie(a, c) {
- var b = 30;
- var d = new Date();
- d.setTime(d.getTime() + b * 24 * 60 * 60 * 1000);
- document.cookie = a + "=" + escape(c) + ";path=/;expires=" + d.toGMTString();
-}
-
-function getCookie(b) {
- var a, c = new RegExp("(^| )" + b + "=([^;]*)(;|$)");
- if(a = document.cookie.match(c)) {
- return unescape(a[2])
- } else {
- return null
- }
-}
-
-function autoHeight() {
- var a = $("body").height() - 40;
- $(".main-content").css("min-height", a);
-}
-
-function showMsg(msg, callback ,icon, time){
-
- if (typeof time == 'undefined'){
- time = 2000;
- }
-
- if (typeof icon == 'undefined'){
- icon = {};
- }
-
- var loadT = layer.msg(msg, icon);
- setTimeout(function() {
- layer.close(loadT);
- if (typeof callback == 'function'){
- callback();
- }
- }, time);
-}
-
-function openPath(a) {
- setCookie("open_dir_path", a);
- window.location.href = "/files/";
-}
-
-function onlineEditFile(k, f, callback) {
- if(k != 0) {
- var l = $("#PathPlace input").val();
- var h = encodeURIComponent($("#textBody").val());
- var a = $("select[name=encoding]").val();
- var loadT = layer.msg('正在保存,请稍候...', {icon: 16,time: 0});
- $.post("/files/save_body", "data=" + h + "&path=" + encodeURIComponent(f) + "&encoding=" + a, function(data) {
- if(k == 1) {
- layer.close(loadT);
- }
- layer.msg(data.msg, {icon: data.status ? 1 : 2});
- if (data.status && typeof(callback) == 'function'){
- callback(k, f);
- }
- },'json');
- return
- }
- var e = layer.msg('正在读取文件,请稍候...', {icon: 16,time: 0});
- var g = f.split(".");
- var b = g[g.length - 1];
- var d;
- switch(b) {
- case "html":
- var j = {
- name: "htmlmixed",
- scriptTypes: [{
- matches: /\/x-handlebars-template|\/x-mustache/i,
- mode: null
- }, {
- matches: /(text|application)\/(x-)?vb(a|script)/i,
- mode: "vbscript"
- }]
- };
- d = j;
- break;
- case "htm":
- var j = {
- name: "htmlmixed",
- scriptTypes: [{
- matches: /\/x-handlebars-template|\/x-mustache/i,
- mode: null
- }, {
- matches: /(text|application)\/(x-)?vb(a|script)/i,
- mode: "vbscript"
- }]
- };
- d = j;
- break;
- case "js":
- d = "text/javascript";
- break;
- case "json":
- d = "application/ld+json";
- break;
- case "css":
- d = "text/css";
- break;
- case "php":
- d = "application/x-httpd-php";
- break;
- case "tpl":
- d = "application/x-httpd-php";
- break;
- case "xml":
- d = "application/xml";
- break;
- case "sql":
- d = "text/x-sql";
- break;
- case "conf":
- d = "text/x-nginx-conf";
- break;
- default:
- var j = {
- name: "htmlmixed",
- scriptTypes: [
- {matches: /\/x-handlebars-template|\/x-mustache/i,mode: null},
- {matches: /(text|application)\/(x-)?vb(a|script)/i,mode: "vbscript"}
- ]
- };
- d = j;
- }
- $.post("/files/get_body", "path=" + encodeURIComponent(f), function(s) {
- if(s.status === false){
- layer.msg(s.msg,{icon:5});
- return;
- }
- layer.close(e);
- var u = ["utf-8", "GBK", "GB2312", "BIG5"];
- var n = "";
- var m = "";
- var o = "";
- for(var p = 0; p < u.length; p++) {
- m = s.data.encoding == u[p] ? "selected" : "";
- n += '";
- }
- var code_mirror = null;
- var r = layer.open({
- type: 1,
- shift: 5,
- closeBtn: 1,
- area: ["90%", "90%"],
- btn:['保存','关闭'],
- title: "在线编辑[" + f + "]",
- content: '',
- success:function(){
- $("#textBody").text(s.data.data);
- var q = $(window).height() * 0.9;
- $("#textBody").height(q - 160);
- code_mirror = CodeMirror.fromTextArea(document.getElementById("textBody"), {
- extraKeys: {
- "Ctrl-F": "findPersistent",
- "Ctrl-H": "replaceAll",
- "Ctrl-S": function() {
- $("#textBody").text(code_mirror.getValue());
- onlineEditFile(2, f,callback);
- },
- "Cmd-S":function() {
- $("#textBody").text(code_mirror.getValue());
- onlineEditFile(2, f,callback);
- },
- },
- mode: d,
- lineNumbers: true,
- matchBrackets: true,
- matchtags: true,
- autoMatchParens: true
- });
- code_mirror.focus();
- code_mirror.setSize("auto", q - 150);
-
- $(window).resize(function(){
- var q = $(window).height() * 0.9;
- code_mirror.setSize("auto", q - 150);
- });
- },
- yes:function(){
- $("#textBody").text(code_mirror.getValue());
- onlineEditFile(1, f,callback);
- }
- });
- },'json');
-}
-
-function divcenter() {
- $(".layui-layer").css("position", "absolute");
- var c = $(window).width();
- var b = $(".layui-layer").outerWidth();
- var g = $(window).height();
- var f = $(".layui-layer").outerHeight();
- var a = (c - b) / 2;
- var e = (g - f) / 2 > 0 ? (g - f) / 2 : 10;
- var d = $(".layui-layer").offset().left - $(".layui-layer").position().left;
- var h = $(".layui-layer").offset().top - $(".layui-layer").position().top;
- a = a + $(window).scrollLeft() - d;
- e = e + $(window).scrollTop() - h;
- $(".layui-layer").css("left", a + "px");
- $(".layui-layer").css("top", e + "px")
-}
-
-function copyText(value) {
- var clipboard = new ClipboardJS('#mw_copys');
- clipboard.on('success', function (e) {
- layer.msg('复制成功',{icon:1,time:2000});
- });
-
- clipboard.on('error', function (e) {
- layer.msg('复制失败,浏览器不兼容!',{icon:2,time:2000});
- });
- $("#mw_copys").attr('data-clipboard-text',value);
- $("#mw_copys").click();
-}
-
-function copyPass(value){
- if (value == ''){
- layer.msg('空,不能复制',{icon:2,time:2000});
- return;
- }
- copyText(value);
-}
-
-function isChineseChar(b) {
- var a = /[\u4E00-\u9FA5\uF900-\uFA2D]/;
- return a.test(b)
-}
-
-function safeMessage(j, h, g, f) {
- if(f == undefined) {
- f = ""
- }
- var d = Math.round(Math.random() * 9 + 1);
- var c = Math.round(Math.random() * 9 + 1);
- var e = "";
- e = d + c;
- sumtext = d + " + " + c;
- setCookie("vcodesum", e);
- var mess = layer.open({
- type: 1,
- title: j,
- area: "350px",
- closeBtn: 1,
- shadeClose: true,
- content: "\
- \
- \
-
| 关闭\
- \
-
| "+lan.public.close+"\
- \
- \
-
\
- 
\
- ';
- }
- var con = '\
- \
- \
- '+g.data[d].addtime+' \
- \
- \
-
\
- \
- \
- \
- '+e+'\
- \
- '+lan.bt.task_name+' '+lan.bt.task_time+' \
-
"
- }
- if(h.task[g].name.indexOf("扫描") != -1) {
- b = "
| 关闭\
- \
-
| 关闭\
-
| "+lan.public.close+"\
- \
- 若任务长时间未执行,请尝试在首页点【重启面板】来重置任务队列';
- $(".taskcon").html(con);
- a = a == undefined ? 1 : a;
- $.post("/task/list", "tojs=getTaskList&table=tasks&limit=10&p=" + a, function(g) {
- var e = "";
- var b = "";
- var c = "";
- var f = false;
- var task_count =0;
- for(var d = 0; d < g.data.length; d++) {
- switch(g.data[d].status) {
- case "-1":
- f = true;
- if(g.data[d].type != "download") {
- b = "
| 关闭\
- \
-
| 关闭\
- [MSG]
";
- var showSpeedKey = layer.open({
- title: false,
- type: 1,
- closeBtn: 2,
- shade: 0.3,
- area: "700px",
- offset: "30%",
- content: speed_msg.replace('[MSG]', msg),
- success: function (layers, index) {
- var url = speed_log_func_name.replace('.','/');
- $.post('/'+url, {}, function(rdata){
- if (rdata.status){
- setTimeout(function () {
- showSpeed(rdata.data);
- }, 1000);
- } else {
- layer.msg("缺少指定文件!");
- }
- },'json');
- if (callback) {callback(layers,index,showSpeedKey);}
- }
- });
-}
-
-
-/*** 其中功能,针对插件通过库使用 start ***/
-
-function toUrlParam(json) {
- return Object.keys(json).map(key => key + '=' + json[key]).join('&');
-}
-
-//字符串转数组对象
-function toArrayObject(str){
- var data = {};
- kv = str.split('&');
- for(i in kv){
- v = kv[i].split('=');
- data[v[0]] = v[1];
- }
- return data;
-}
-
-/**
-* 实体字符编码
-* @param {*} text 待编码的文本
-* @returns
-*/
-function entitiesEncode(text) {
- text = text.replace(/&/g, "&");
- text = text.replace(//g, ">");
- text = text.replace(/ /g, " ");
- text = text.replace(/"/g, """);
- return text;
-}
-/**
-* 实体字符解码
-* @param {*} text 待解码的文本
-* @returns
-*/
-function entitiesDecode(text) {
- text = text.replace(/&/g, "&");
- text = text.replace(/</g, "<");
- text = text.replace(/>/g, ">");
- text = text.replace(/ /g, " ");
- text = text.replace(/"/g, "'");
- return text;
-}
-
-// base64.js
-// base64加密
-function base64_encode(str) {
- var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- var out, i, len;
- var c1, c2, c3;
-
- len = str.length;
- i = 0;
- out = "";
- while(i < len) {
- c1 = str.charCodeAt(i++) & 0xff;
- if(i == len)
- {
- out += base64EncodeChars.charAt(c1 >> 2);
- out += base64EncodeChars.charAt((c1 & 0x3) << 4);
- out += "==";
- break;
- }
- c2 = str.charCodeAt(i++);
- if(i == len)
- {
- out += base64EncodeChars.charAt(c1 >> 2);
- out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
- out += base64EncodeChars.charAt((c2 & 0xF) << 2);
- out += "=";
- break;
- }
- c3 = str.charCodeAt(i++);
- out += base64EncodeChars.charAt(c1 >> 2);
- out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
- out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6));
- out += base64EncodeChars.charAt(c3 & 0x3F);
- }
- return out;
-}
-
-
-// base64解密
-function base64_decode(str) {
- var base64DecodeChars = new Array(
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
- -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
- -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
- 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);
- var c1, c2, c3, c4;
- var i, len, out;
-
- len = str.length;
- i = 0;
- out = "";
- while(i < len) {
- /* c1 */
- do {
- c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
- } while(i < len && c1 == -1);
- if(c1 == -1)
- break;
-
- /* c2 */
- do {
- c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
- } while(i < len && c2 == -1);
- if(c2 == -1)
- break;
-
- out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
-
- /* c3 */
- do {
- c3 = str.charCodeAt(i++) & 0xff;
- if(c3 == 61)
- return out;
- c3 = base64DecodeChars[c3];
- } while(i < len && c3 == -1);
- if(c3 == -1)
- break;
-
- out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
-
- /* c4 */
- do {
- c4 = str.charCodeAt(i++) & 0xff;
- if(c4 == 61)
- return out;
- c4 = base64DecodeChars[c4];
- } while(i < len && c4 == -1);
- if(c4 == -1)
- break;
- out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
- }
- return out;
-}
-
-function utf16to8(str) {
- var out, i, len, c;
-
- out = "";
- len = str.length;
- for(i = 0; i < len; i++) {
- c = str.charCodeAt(i);
- if ((c >= 0x0001) && (c <= 0x007F)) {
- out += str.charAt(i);
- } else if (c > 0x07FF) {
- out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
- out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
- out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
- } else {
- out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
- out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
- }
- }
- return out;
-}
-
-function utf8to16(str) {
- var out, i, len, c;
- var char2, char3;
-
- out = "";
- len = str.length;
- i = 0;
- while(i < len) {
- c = str.charCodeAt(i++);
- switch(c >> 4)
- {
- case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
- // 0xxxxxxx
- out += str.charAt(i-1);
- break;
- case 12: case 13:
- // 110x xxxx 10xx xxxx
- char2 = str.charCodeAt(i++);
- out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
- break;
- case 14:
- // 1110 xxxx 10xx xxxx 10xx xxxx
- char2 = str.charCodeAt(i++);
- char3 = str.charCodeAt(i++);
- out += String.fromCharCode(((c & 0x0F) << 12) |
- ((char2 & 0x3F) << 6) |
- ((char3 & 0x3F) << 0));
- break;
- }
- }
-
- return out;
-}
-
-
-function pluginService(_name, version, _suffix_name=''){
-
- var default_name = 'status';
- if ( _suffix_name != '' ){
- default_name = 'status_'+_suffix_name;
- }
-
- var data = {name:_name, func:default_name};
- if ( typeof(version) != 'undefined' ){
- data['version'] = version;
- } else {
- version = '';
- }
-
- var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
- $.post('/plugins/run', data, function(data) {
- layer.close(loadT);
- if(!data.status){
- layer.msg(data.msg,{icon:0,time:3000,shade: [0.3, '#000']});
- return;
- }
- if (data.data == 'start'){
- pluginSetService(_name, true, version, _suffix_name);
- } else {
- pluginSetService(_name, false, version, _suffix_name);
- }
- },'json');
-}
-
-function pluginSetService(_name ,status, version, _suffix_name=''){
-
- var default_name = 'status';
- var restart_name = 'restart';
- var reload_name = 'reload';
- var status_ss = (status?'stop':'start');
- if ( _suffix_name != '' ){
- default_name = 'status_'+_suffix_name;
- restart_name = 'restart_'+_suffix_name;
- reload_name = 'reload_'+_suffix_name;
- status_ss = status_ss+'_'+_suffix_name;
- }
-
- var serviceCon ='\
-
';
-
-
- var loadT = layer.msg('配置文件路径获取中...',{icon:16,time:0,shade: [0.3, '#000']});
- $.post('/plugins/run', {name:_name, func:func_name,version:version},function (data) {
- layer.close(loadT);
-
- try{
- var jdata = $.parseJSON(data.data);
- if (!jdata['status']){
- layer.msg(jdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
- return;
- }
- }catch(err){/*console.log(err);*/}
-
- $(".soft-man-con").html(con);
-
- var loadT2 = layer.msg('文件内容获取中...',{icon:16,time:0,shade: [0.3, '#000']});
- var fileName = data.data;
- $.post('/files/get_body', 'path=' + fileName, function(rdata) {
- layer.close(loadT2);
- if (!rdata.status){
- layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
- return;
- }
- $("#textBody").empty().text(rdata.data.data);
- $(".CodeMirror").remove();
- var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), {
- extraKeys: {
- "Ctrl-Space": "autocomplete",
- "Ctrl-F": "findPersistent",
- "Ctrl-H": "replaceAll",
- "Ctrl-S": function() {
- $("#textBody").text(editor.getValue());
- pluginConfigSave(fileName);
- }
- },
- lineNumbers: true,
- matchBrackets:true,
- });
- editor.focus();
- $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0});
- $("#onlineEditFileBtn").click(function(){
- $("#textBody").text(editor.getValue());
- pluginConfigSave(fileName);
- });
- },'json');
- },'json');
-}
-
-
-//配置修改模版 --- start
-function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_func, save_callback_func){
- if ( typeof(version) == 'undefined' ){
- version = '';
- }
-
- var func_name = 'conf';
- if ( typeof(func) != 'undefined' ){
- func_name = func;
- }
-
- var _config_tpl_func = 'config_tpl';
- if ( typeof(config_tpl_func) != 'undefined' ){
- _config_tpl_func = config_tpl_func;
- }
-
- var _read_config_tpl_func = 'read_config_tpl';
- if ( typeof(read_config_tpl_func) != 'undefined' ){
- _read_config_tpl_func = read_config_tpl_func;
- }
-
-
- var con = '\
-
';
- $(".soft-man-con").html(con);
-
- function getFileName(file){
- var list = file.split('/');
- var f = list[list.length-1];
- return f
- }
-
- var fileName = '';
- $.post('/plugins/run',{name:_name, func:_config_tpl_func,version:version}, function(data){
- var rdata = $.parseJSON(data.data);
- for (var i = 0; i < rdata.length; i++) {
- $('#config_tpl').append('');
- }
-
- $('#config_tpl').change(function(){
- var selected = $(this).val();
- if (selected != '0'){
- var loadT = layer.msg('配置模版获取中...',{icon:16,time:0,shade: [0.3, '#000']});
-
- var _args = JSON.stringify({file:selected});
- $.post('/plugins/run', {name:_name, func:_read_config_tpl_func,version:version,args:_args}, function(data){
- layer.close(loadT);
- var rdata = $.parseJSON(data.data);
- if (!rdata.status){
- layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
- return;
- }
-
- $("#textBody").empty().text(rdata.data);
- $(".CodeMirror").remove();
- var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), {
- extraKeys: {
- "Ctrl-Space": "autocomplete",
- "Ctrl-F": "findPersistent",
- "Ctrl-H": "replaceAll",
- "Ctrl-S": function() {
- $("#textBody").text(editor.getValue());
- pluginConfigSave(fileName,save_callback_func);
- }
- },
- lineNumbers: true,
- matchBrackets:true,
- });
- editor.focus();
- $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0});
- $("#onlineEditFileBtn").unbind('click');
- $("#onlineEditFileBtn").click(function(){
- $("#textBody").text(editor.getValue());
- pluginConfigSave(fileName, save_callback_func);
- });
- },'json');
- }
- });
-
- },'json');
-
- var loadT = layer.msg('配置文件路径获取中...',{icon:16,time:0,shade: [0.3, '#000']});
- $.post('/plugins/run', {name:_name, func:func_name,version:version}, function (data) {
- layer.close(loadT);
-
- var loadT2 = layer.msg('文件内容获取中...',{icon:16,time:0,shade: [0.3, '#000']});
- fileName = data.data;
- $.post('/files/get_body', 'path=' + fileName, function(rdata) {
- layer.close(loadT2);
- if (!rdata.status){
- layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
- return;
- }
- $("#textBody").empty().text(rdata.data.data);
- $(".CodeMirror").remove();
- var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), {
- extraKeys: {
- "Ctrl-Space": "autocomplete",
- "Ctrl-F": "findPersistent",
- "Ctrl-H": "replaceAll",
- "Ctrl-S": function() {
- $("#textBody").text(editor.getValue());
- pluginConfigSave(fileName,save_callback_func);
- }
- },
- lineNumbers: true,
- matchBrackets:true,
- });
- editor.focus();
- $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0});
- $("#onlineEditFileBtn").click(function(){
- $("#textBody").text(editor.getValue());
- pluginConfigSave(fileName,save_callback_func);
- });
- },'json');
- },'json');
-}
-
-//配置模版列表 --- start
-function pluginConfigListTpl(_name, version, config_tpl_func, read_config_tpl_func){
- if ( typeof(version) == 'undefined' ){
- version = '';
- }
-
- var _config_tpl_func = 'config_tpl';
- if ( typeof(config_tpl_func) != 'undefined' ){
- _config_tpl_func = config_tpl_func;
- }
-
- var _read_config_tpl_func = 'read_config_tpl';
- if ( typeof(read_config_tpl_func) != 'undefined' ){
- _read_config_tpl_func = read_config_tpl_func;
- }
-
-
- var con = '\
-
';
- $(".soft-man-con").html(con);
-
- function getFileName(file){
- var list = file.split('/');
- var f = list[list.length-1];
- return f
- }
-
- function loadTextBody(fileName){
- $.post('/files/get_body', 'path=' + fileName, function(rdata) {
- if (!rdata.status){
- layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
- return;
- }
- $("#textBody").empty().text(rdata.data.data);
- $(".CodeMirror").remove();
- var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), {
- extraKeys: {
- "Ctrl-Space": "autocomplete",
- "Ctrl-F": "findPersistent",
- "Ctrl-H": "replaceAll",
- "Ctrl-S": function() {
- $("#textBody").text(editor.getValue());
- pluginConfigSave(fileName);
- }
- },
- lineNumbers: true,
- matchBrackets:true,
- });
- editor.focus();
- $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0});
- $("#onlineEditFileBtn").click(function(){
- $("#textBody").text(editor.getValue());
- pluginConfigSave(fileName);
- });
- },'json');
- }
-
-
-
- var fileName = '';
- $.post('/plugins/run',{name:_name, func:_config_tpl_func,version:version}, function(data){
- var rdata = $.parseJSON(data.data);
- for (var i = 0; i < rdata.length; i++) {
- $('#config_tpl').append('');
- }
-
- if (rdata.length>0){
- loadTextBody(rdata[0]);
- }
-
- $('#config_tpl').change(function(){
- var selected = $(this).val();
- fileName = selected;
-
- var loadT = layer.msg('配置模版获取中...',{icon:16,time:0,shade: [0.3, '#000']});
-
- var _args = JSON.stringify({file:selected});
- $.post('/plugins/run', {name:_name, func:_read_config_tpl_func,version:version,args:_args}, function(data){
- layer.close(loadT);
- var rdata = $.parseJSON(data.data);
- if (!rdata.status){
- layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
- return;
- }
-
- $("#textBody").empty().text(rdata.data);
- $(".CodeMirror").remove();
- var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), {
- extraKeys: {
- "Ctrl-Space": "autocomplete",
- "Ctrl-F": "findPersistent",
- "Ctrl-H": "replaceAll",
- "Ctrl-S": function() {
- $("#textBody").text(editor.getValue());
- pluginConfigSave(fileName);
- }
- },
- lineNumbers: true,
- matchBrackets:true,
- });
- editor.focus();
- $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0});
- $("#onlineEditFileBtn").unbind('click');
- $("#onlineEditFileBtn").click(function(){
- $("#textBody").text(editor.getValue());
- pluginConfigSave(fileName);
- });
- },'json');
-
- });
-
- },'json');
-}
-
-
-//配置保存
-function pluginConfigSave(fileName, callback) {
- var data = encodeURIComponent($("#textBody").val());
- var encoding = 'utf-8';
- var loadT = layer.msg('保存中...', {icon: 16,time: 0});
- $.post('/files/save_body', 'data=' + data + '&path=' + fileName + '&encoding=' + encoding, function(rdata) {
- layer.close(loadT);
-
- showMsg(rdata.msg, function(){
- if ( rdata.status && typeof(callback) == 'function'){
- callback();
- }
- },{icon: rdata.status ? 1 : 2});
-
- },'json');
-}
-
-function pluginInitD(_name, _version, _suffix_name=''){
- if (typeof _version == 'undefined'){
- _version = '';
- }
-
- var default_name = 'initd_status';
- if ( _suffix_name != '' ){
- default_name = 'initd_status_'+_suffix_name;
- }
-
- var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
- $.post('/plugins/run', {name:_name, func:default_name, version : _version}, function(data) {
- layer.close(loadT);
- if( !data.status ){
- layer.msg(data.msg,{icon:0,time:3000,shade: [0.3, '#000']});
- return;
- }
- if( data.data!='ok' && data.data!='fail' ){
- layer.msg(data.data,{icon:0,time:3000,shade: [0.3, '#000']});
- return;
- }
- if (data.data == 'ok'){
- pluginSetInitD(_name, _version, true, _suffix_name);
- } else {
- pluginSetInitD(_name, _version, false, _suffix_name);
- }
- },'json');
-}
-
-function pluginSetInitD(_name, _version, status,_suffix_name=''){
-
- var default_name = (status?'initd_uninstall':'initd_install');
- if ( _suffix_name != '' ){
- default_name = default_name + '_' + _suffix_name;
- }
-
- var serviceCon =' "
-
- $("#webBody").append(body);
- //setEdate(data.data[i].id,data.data[i].edate);
- //设置到期日期
- function getDate(a) {
- var dd = new Date();
- dd.setTime(dd.getTime() + (a == undefined || isNaN(parseInt(a)) ? 0 : parseInt(a)) * 86400000);
- var y = dd.getFullYear();
- var m = dd.getMonth() + 1;
- var d = dd.getDate();
- return y + "-" + (m < 10 ? ('0' + m) : m) + "-" + (d < 10 ? ('0' + d) : d);
- }
- $('#webBody').on('click','#site_'+ data.data[i].id,function(){
- var _this = $(this);
- var id = $(this).attr('data-ids');
- laydate.render({
- elem: '#site_'+ id //指定元素
- ,min:getDate(1)
- ,max:'2099-12-31'
- ,vlue:getDate(365)
- ,type:'date'
- ,format :'yyyy-MM-dd'
- ,trigger:'click'
- ,btns:['perpetual', 'confirm']
- ,theme:'#20a53a'
- ,done:function(dates){
- if(_this.html() == '永久'){
- dates = '0000-00-00';
- }
- var loadT = layer.msg(lan.site.saving_txt, { icon: 16, time: 0, shade: [0.3, "#000"]});
- $.post('/site/set_end_date','id='+id+'&edate='+dates,function(rdata){
- layer.close(loadT);
- layer.msg(rdata.msg,{icon:rdata.status?1:5});
- },'json');
- }
- });
- this.click();
- });
- }
- if(body.length < 10){
- body = "\
- " + shortwebname + " \
- " + status + " \
- " + backup + " \
- " + shortpath + " \
- " + web_end_time + " \
- " + data.data[i].ps + " \
- \
- 设置\
- | 删除\
- ";
- // $(".dataTables_paginate").hide();
- $("#webBody").html(body);
- }
- //输出数据列表
- $(".btn-more").hover(function(){
- $(this).addClass("open");
- },function(){
- $(this).removeClass("open");
- });
-
- //输出分页
- $("#webPage").html(data.page);
- // $("#webPage").html('当前没有站点数据 \
-
"
- +'
\
- \
-
\
- \
- ";
- }
- var bodyHtml = "\
- \
- \
- " + domain[i].name + " \
- " + domain[i].port + " \
- \
- \
-
\
- \
- " + echoHtml + "\
- "+lan.site.domain+" 端口 操作
泛解析添加方法 *.domain.com
如另加端口格式为 www.domain.com:88 "
- }
-
- var ftpdown = '';
- frdata.page = frdata.page.replace(/'/g,'"').replace(/getBackup\(/g,"getBackup(" + id + ",0,");
-
- if(name == 0){
- var sBody = ""+frdata.data[i].name+" \
- " + (toSize(frdata.data[i].size)) + " \
- " + frdata.data[i].addtime + " \
- "+ ftpdown + "删除 \
- \
-
"
- $("#webBackupList").html(sBody);
- $(".page").html(frdata.page);
- return;
- }
- layer.closeAll();
- layer.open({
- type: 1,
- skin: 'demo-class',
- area: '700px',
- title: '打包备份',
- closeBtn: 1,
- shift: 0,
- shadeClose: false,
- content: " \
- "+body+"\
- 文件名称 文件大小 打包时间 操作 \
- \
-
\
- \
- \
- " + body + "\
- 文件名称 文件大小 打包时间 操作 \
-
\
- \
-
\
-
泛解析添加方法 *.domain.com
如另加端口格式为 www.domain.com:88'
- +'
'
- +'
"
- $("#webedit-con").html(body);
-
- $("select[name='limit']").change(function(){
- var type = $(this).val();
- perserver = 300;
- perip = 25;
- limit_rate = 512;
- switch(type){
- case '1':
- perserver = 300;
- perip = 25;
- limit_rate = 512;
- break;
- case '2':
- perserver = 200;
- perip = 10;
- limit_rate = 1024;
- break;
- case '3':
- perserver = 50;
- perip = 3;
- limit_rate = 2048;
- break;
- case '4':
- perserver = 500;
- perip = 10;
- limit_rate = 2048;
- break;
- case '5':
- perserver = 400;
- perip = 15;
- limit_rate = 1024;
- break;
- case '6':
- perserver = 60;
- perip = 10;
- limit_rate = 512;
- break;
- case '7':
- perserver = 150;
- perip = 4;
- limit_rate = 1024;
- break;
- }
-
- $("input[name='perserver']").val(perserver);
- $("input[name='perip']").val(perip);
- $("input[name='limit_rate']").val(limit_rate);
- });
- },'json');
-}
-
-
-//保存流量限制配置
-function saveLimitNet(id, type){
- var isChecked = $("input[name='status']").attr('checked');
- if(isChecked == undefined || type == 1 ){
- var data = 'id='+id+'&perserver='+$("input[name='perserver']").val()+'&perip='+$("input[name='perip']").val()+'&limit_rate='+$("input[name='limit_rate']").val();
- var loadT = layer.msg(lan.public.config,{icon:16,time:10000})
- $.post('/site/save_limit_net',data,function(rdata){
- layer.close(loadT);
- limitNet(id);
- layer.msg(rdata.msg,{icon:rdata.status?1:2});
- },'json');
- }else{
- var loadT = layer.msg(lan.public.config,{icon:16,time:10000})
- $.post('/site/close_limit_net',{id:id},function(rdata){
- layer.close(loadT);
- limitNet(id);
- layer.msg(rdata.msg,{icon:rdata.status?1:2});
- },'json');
- }
-}
-
-
-//子目录绑定
-function dirBinding(id){
- $.post('/site/get_dir_binding',{'id':id},function(data){
- var rdata = data['data'];
- var echoHtml = '';
- for(var i=0;i"+rdata.binding[i].domain+" "+rdata.binding[i].port+" "+rdata.binding[i].path+" 伪静态 | 删除 ";
- }
-
- var dirList = '';
- for(var n=0;n"
- + "
"
- + "" + echoHtml + ""
- + "域名 端口 子目录 操作 \
-
\
- \
-
\
- \
- \
-
\
- \
- \
- \
- \
- \
- 重定向类型 \
- 重定向方式 \
- 保留URL参数 \
- 操作 \
- \
- ';
- $("#md-301-body").append(tmp);
- })
- } else {
- layer.msg(res.msg, {icon:2});
- }
- },'json');
-}
-
-
-//反向代理
-function toProxy(siteName, type, obj) {
- // 设置 页面展示
- if(type == 1) {
- var proxy_title = "创建反向代理";
- if (typeof(obj) != 'undefined'){
- proxy_title = "编辑反向代理";
- }
-
- layer.open({
- type: 1,
- area: '650px',
- title: proxy_title,
- closeBtn: 1,
- shift: 5,
- shadeClose: false,
- btn: ['提交','关闭'],
- content: "",
- success:function(){
-
- if (typeof(obj) != 'undefined'){
- // console.log(obj);
- $('input[name="name"]').val(obj['name']).attr('readonly','readonly').addClass('disabled');
- if (obj['open_cache'] == 'on'){
- $("input[name='open_cache']").prop("checked",true);
- $('#cache_time').show();
- }
-
- $('input[name="from"]').val(obj['from']);
- $('input[name="to"]').val(obj['to']);
-
- var url = obj['to'];
- var 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])$/;
- url = url.replace(/^http[s]?:\/\//, '');
- url = url.replace(/(:|\?|\/|\\)(.*)$/, '');
- if (ip_reg.test(url)) {
- $("[name='host']").val('$host');
- } else {
- $("[name='host']").val(url);
- }
-
- $('input[name="id"]').val(obj['id']);
- $('input[name="cache_time"]').val(obj['cache_time']);
- }
-
-
- $('input[name="to"]').on('keyup', function(){
- var url = $(this).val();
- var 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])$/;
- url = url.replace(/^http[s]?:\/\//, '');
- url = url.replace(/(:|\?|\/|\\)(.*)$/, '');
- if (ip_reg.test(url)) {
- $("[name='host']").val('$host');
- } else {
- $("[name='host']").val(url);
- }
- });
-
- $("#open_proxy").click(function(){
- var status = $("input[name='open_proxy']").prop("checked")==true?1:0;
- if(status==1){
- $("input[name='open_proxy']").prop("checked",false);
- }else{
- $("input[name='open_proxy']").prop("checked",true);
- }
- });
-
- $('#open_cache').click(function(){
- var status = $("input[name='open_cache']").prop("checked")==true?1:0;
- if(status==1){
- $('#cache_time').hide();
- $("input[name='open_cache']").prop("checked",false);
- }else{
- $('#cache_time').show();
- $("input[name='open_cache']").prop("checked",true);
- }
- });
- },
- yes:function(index,layer_ro){
- var data = $('#form_proxy').serializeArray();
- var t = {};
- t['name'] = 'siteName';
- t['value'] = siteName;
- data.push(t);
-
- // console.log(data);
- var loading = layer.msg('正在'+proxy_title+'...',{icon:16,time:0,shade: [0.3, '#000']});
- $.post('/site/set_proxy',data, function(res) {
- layer.close(loading);
- if (!res.status){
- layer.msg(res.msg, {icon: 2,time:10000});
- return;
- }
-
- showMsg(proxy_title+"成功!",function(){
- layer.close(index);
- toProxy(siteName);
- },{icon: 1, time:2000});
- },'json');
- }
- });
- }
-
- if (type == 2) {
- var loading = layer.msg('正在删除中...',{icon:16,time:0,shade: [0.3, '#000']});
- $.post('/site/del_proxy', {siteName: siteName,id: obj,}, function(res) {
- layer.close(loading);
- if (res.status == true) {
- showMsg('删除成功', function(){
- toProxy(siteName);
- },{time: 1000,icon: 1});
- } else {
- layer.msg(res.msg, {time: 1000,icon: 2});
- }
- },'json');
- return
- }
-
-
- if (type == 3) {
- var laoding = layer.load();
- var data = {siteName: siteName,id: obj};
- $.post('/site/get_proxy_conf', data, function(res) {
- layer.close(laoding);
- if (!res.status){
- layer.msg('请求错误!!', {time: 3000,icon: 2});
- return;
- }
-
- var mBody = "'+item.r_from+' \
- '+lan_r_type+' \
- '+keep_path+' \
- 详细 | 删除 \
- \
-
\
- \
- \
-
\
- \
- \
- \
- \
- 名称 \
- 代理目录 \
- 目标地址 \
- 缓存 \
- 状态 \
- 操作 \
- \
- ';
- $("#md-301-body").append(tmp);
- }
-
- $('#md-301-body .detail').click(function(){
- var index = $(this).data('index');
- toProxy(siteName, 3 ,data[index]['id']);
- });
-
- $('#md-301-body .edit').click(function(){
- var index = $(this).data('index');
- toProxy(siteName, 1 ,data[index]);
- });
-
- $('#md-301-body .delete').click(function(){
- var index = $(this).data('index');
- toProxy(siteName, 2 ,data[index]['id']);
- });
-
- $('#md-301-body .cache').click(function(){
- var index = $(this).data('index');
- if ($(this).hasClass('on')){
- toProxy(siteName, 21 ,data[index]);
- } else{
- toProxy(siteName, 20 ,data[index]);
- }
- });
-
- },'json');
-/////////
-}
-
-//证书夹
-function sslAdmin(siteName){
- var loadT = layer.msg('正在提交任务...',{icon:16,time:0,shade: [0.3, '#000']});
- $.get('/site/get_cert_list',function(data){
- layer.close(loadT);
- var rdata = data['data'];
- var tbody = '';
- for(var i=0;i'+item.name+' \
- '+item.from+' \
- '+item.to+' \
- '+openCache+' \
- '+switchProxy+' \
- \
- 详细 |\
- 编辑 |\
- 删除\
- \
- '+rdata[i].subject+' \
- '+rdata[i].dns.join(' \
-
')+''+rdata[i].notAfter+' \
- '+rdata[i].issuer.split(' ')[0]+' \
- 部署 | 删除 \
- '
- }
- var txt = ' \
- '+tbody+'\
- 域名 信任名称 到期时间 品牌 操作 \
-
';
-
- // \
- // \
- var lets = '\
-
\
-
\
- \
-
\
-
\
- \
- \
-
';
- $(".tab-con").html(lets);
-
- if (rdata['cert_data']){
- var issuer = rdata['cert_data']['issuer'].split(" ");
- var domains = rdata['cert_data']['dns'].join("、");
-
- var cert_data = "\
-
';
- $(".tab-con").html(acme);
-
- if (rdata['cert_data']){
- var issuer = rdata['cert_data']['issuer'].split(" ");
- var domains = rdata['cert_data']['dns'].join("、");
-
- var cert_data = "
"
- for(var i=0;i
错误类型: "+rdata.out[i].Type+"
" - + "详情: "+rdata.out[i].Detail+"
" - + "| 名称 | 操作 |
|---|
' + plugin_title + '
名称:'+ data.title + '
\ -版本:' + data.versions +'
\ -描述:' + data.ps + '
\ -大小:' + toSize(data.size) + '
\ -作者:' + data.author + '
\ - \ -"+lan.upload.up_the+ this.num + "/" + i.length + "
"); - $(".cancel").css("visibility", "hidden") - } - this.send(g, i, f, h) - }, - SetTxt: function(f, g, h) { - var e = this.up_box_li[f].getElementsByTagName("em")[0]; - e.style.color = h; - e.innerHTML = g - }, - send: function(f, i, e, g) { - if(!this.up_box_li[e].getElementsByTagName("em")[0]) { - this.ready(i, e + 1, g); - this.num++; - return - } - var h = this; - this.FileProgress = 0; - this.str.carry({ - url: this.url, - data: f, - type: "get", - timeout: 86400000, - async: true, - lock: true, - complete: false, - progress: function(j) { - h.FileProgress = Math.floor(j.loaded / j.total * 100) + "%"; - if(h.FileProgress == "100%") { - h.FileProgress = '正在保存..'; - } - h.SetTxt(i[e][1], '上传进度:' + h.FileProgress, "#005100") - }, - success: function(j) { - h.str.serverdata = false; - h.SetTxt(i[e][1], '已上传成功', "#005100"); - h.ready(i, e + 1, g); - h.num++; - if(i.length > 1) { - var k = (h.num == i.length) ? '上传完成': '已上传'; - $("#totalProgress").html("" + k + h.num + "/" + i.length + "
") - } - if(h.num == i.length) { - c.opt.disabled = false; - c.up.disabled = false; - c.file_input.disabled = false; - h.num = 0 - } - if(!d) { - getFiles(getCookie("open_dir_path")); - } else{ - d(); - } - }, - error: function(j) { - h.SetTxt(i[e][1], lan.upload.up_err, "red"); - h.str.serverdata = false; - h.ready(i, e + 1, g) - }, - cache: false - }) - } - }; - try { - var c = new a({ - UpType: new Array(), - FilesSize: 5242880000, - MaxUpNum: 100, - url: "/files/upload_file?path=" + encodeURIComponent(document.getElementById("input-val").value) - }); - c.opt.addEventListener("click", function() { - c.file_input.click() - }, false); - c.up.addEventListener("click", function() { - c.read(); - }, false); - c.file_input.addEventListener("change", function() { - c.SelectFile(); - }, false) - } catch(b) { - c.opt.disabled = true; - c.up.disabled = true; - c.file_input.disabled = true; - layer.msg('抱歉,IE 6/7/8 不支持请更换浏览器再上传', {icon: 5}); - } -}; \ No newline at end of file diff --git a/route/static/bootstrap-3.3.5/css/bootstrap-theme.min.css b/route/static/bootstrap-3.3.5/css/bootstrap-theme.min.css deleted file mode 100755 index 61358b13d..000000000 --- a/route/static/bootstrap-3.3.5/css/bootstrap-theme.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} \ No newline at end of file diff --git a/route/static/bootstrap-3.3.5/css/bootstrap.min.css b/route/static/bootstrap-3.3.5/css/bootstrap.min.css deleted file mode 100755 index 0336abcd4..000000000 --- a/route/static/bootstrap-3.3.5/css/bootstrap.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before,.glyphicon-True:before{content:"\e072"}.glyphicon-pause:before,.glyphicon-False:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:none}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:none}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-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}.form-control:focus{border-color:#66afe9;outline:0;-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)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 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:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:none}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#555;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#fff;background-color:#10952a;border-color:#398439}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#fff;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#20a53a;border-color:#20a53a}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#10952a;border-color:#255625}.btn-success:hover{color:#fff;background-color:#10952a;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#10952a;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#20a53a;border-color:#20a53a}.btn-success .badge{color:#20a53a;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.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-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;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.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#20a53a}.label-success[href]:focus,.label-success[href]:hover{background-color:#10952a}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#20a53a}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/route/static/bootstrap-3.3.5/css/index.html b/route/static/bootstrap-3.3.5/css/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/bootstrap-3.3.5/css/index.html +++ /dev/null @@ -1,13 +0,0 @@ -目标URL
-发送域名
-内容替换
- -目标URL
-发送域名
-内容替换
- -目标URL
-发送域名
-内容替换
- -- Attach the terminal to a WebSocket terminal stream with ease. Perfect for attaching to your - Docker containers. -
-- - -
-- -
-- -
-- -
-- -
-- -
-- -
-Attention: The demo is a barebones implementation and is designed for xterm.js evaluation purposes only. Exposing the demo to the public as is would introduce security risks for the host.
-* ZMODEM file transfers are supported via an addon. To try it out, install lrzsz onto the remote peer, then run rz to send from your browser or sz <file> to send from the remote peer.